I have three main tables to keep track of products, location and the logistics between them which includes moving products to and from various locations. I have made another table balance to keep a final balance of the quantity of each product in respective locations.
Here are the schemas:
products(prod_id INTEGER PRIMARY KEY AUTOINCREMENT,
prod_name TEXT UNIQUE NOT NULL,
prod_quantity INTEGER NOT NULL,
unallocated_quantity INTEGER)
Initially, when products are added, prod_quantity and unallocated_quantity have the same values. unallocated_quantity is then subtracted from, each time a certain quantity of the respective product is allocated.
location(loc_id INTEGER PRIMARY KEY AUTOINCREMENT,
loc_name TEXT UNIQUE NOT NULL)
logistics(trans_id INTEGER PRIMARY KEY AUTOINCREMENT,
prod_id INTEGER NOT NULL,
from_loc_id INTEGER NULL,
to_loc_id INTEGER NOT NULL,
prod_quantity INTEGER NOT NULL,
trans_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(prod_id) REFERENCES products(prod_id),
FOREIGN KEY(from_loc_id) REFERENCES location(loc_id),
FOREIGN KEY(to_loc_id) REFERENCES location(loc_id))
balance(prod_id INTEGER NOT NULL,
loc_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
FOREIGN KEY(prod_id) REFERENCES products(prod_id),
FOREIGN KEY(loc_id) REFERENCES location(loc_id))
At each entry made in logistics, I want a trigger to update the values in balance thereby keeping a summary of all the transactions (moving products between locations)
I thought of a trigger solution which checks if for each insert on the table logistics, there already exists the same prod_id, loc_id entry in the balance table, which if exists will be updated appropriately. However, I don't have the experience in SQLite to implement this idea.
I believe that your TRIGGER would be along the lines of either :-
CREATE TRIGGER IF NOT EXISTS logistics_added AFTER INSERT ON logistics
BEGIN
UPDATE balance SET quantity = ((SELECT quantity FROM balance WHERE prod_id = new.prod_id AND loc_id = new.from_loc_id) - new.prod_quantity) WHERE prod_id = new.prod_id AND loc_id = new.from_loc_id;
UPDATE balance SET quantity = ((SELECT quantity FROM balance WHERE prod_id = new.prod_id AND loc_id = new.to_loc_id) + new.prod_quantity) WHERE prod_id = new.prod_id AND loc_id = new.to_loc_id;
END;
or :-
CREATE TRIGGER IF NOT EXISTS logistics_added AFTER INSERT ON logistics
BEGIN
INSERT OR REPLACE INTO balance VALUES(new.prod_id,new.from_loc_id,(SELECT quantity FROM balance WHERE prod_id = new.prod_id AND loc_id = new.from_loc_id) - new.prod_quantity);
INSERT OR REPLACE INTO balance VALUES(new.prod_id,new.to_loc_id,(SELECT quantity FROM balance WHERE prod_id = new.prod_id AND loc_id = new.to_loc_id) + new.prod_quantity);
END;
Note that the second relies upon adding a UNIQUE constraint to the balance table by using PRIMARY KEY (prod_id,loc_id) or alternately UNIQUE (prod_id,loc_id). The UNIQUE constraint would probably be required/wanted anyway.
The subtle difference is that the second would INSERT a balance row if and appropriate one didn't exist. The latter would do nothing if the appropriate balance row didn't exist.
Related
i have 3 table in my database
CREATE TABLE IF NOT EXISTS depances (
id SERIAL PRIMARY KEY UNIQUE NOT NULL,
type VARCHAR NOT NULL,
nom VARCHAR,
montant DECIMAL(100,2) NOT NULL,
date DATE,
temp TIME)
CREATE TABLE IF NOT EXISTS transactions (
id SERIAL PRIMARY KEY UNIQUE NOT NULL,
montant DECIMAL(100,2),
medecin VARCHAR,
patient VARCHAR,
acte VARCHAR,
date_d DATE,
time_d TIME,
users_id INTEGER)
CREATE TABLE IF NOT EXISTS total_jr (
id SERIAL PRIMARY KEY UNIQUE NOT NULL,
total_revenu DECIMAL(100,2),
total_depance DECIMAL(100,2),
total_différence DECIMAL(100,2),
date DATE)
my idea is to insert defrent value in table depances and transaction using a GUI interface.
and after that adding the SUM of montant.depances in total_depance.total_jr
and the SUM of montant.transactions in total_revenu.total_jr where all rows have the same time
that's the easy part using this code
self.cur.execute( '''SELECT SUM(montant) AS totalsum FROM depances WHERE date = %s''',(date,))
result = self.cur.fetchall()
for i in result:
o = i[0]
self.cur_t = self.connection.cursor()
self.cur_t.execute( '''INSERT INTO total_jr(total_depance)
VALUES (%s)'''
, (o,))
self.connection.commit()
self.cur.execute( '''UPDATE total_jr SET total_depance = %s WHERE date = %s''',(o, date))
self.connection.commit()
But every time it adds a new row to the table of total_jr
How can i add thos value of SUM(montant) to the table where the date is the same every time its only put the value of sum in one row not every time it add a new row
The result should will be like this
id|total_revenu|total_depance|total_différence|date
--+------------+-------------+----------------+----
1 sum(montant1) value value 08/07/2020
2 sum(montant2) value value 08/09/2020
3 sum(montant3) value value 08/10/2020
but it only give me this result
id|total_revenu|total_depance|total_différence|date
--+------------+-------------+----------------+----
1 1 value value 08/07/2020
2 2 value value 08/07/2020
3 3 value value 08/7/2020
if there is any idea or any hit that will be hulpefull
You didn't mention which DBMS or SQL module you're using so I'm guessing MySQL.
In your process, run the update first and check how many rows were changed. If zero row changed, then insert a new row for that date.
self.cur.execute( '''SELECT SUM(montant) AS totalsum FROM depances WHERE date = %s''',(date,))
result = self.cur.fetchall()
for i in result:
o = i[0]
self.cur.execute( '''UPDATE total_jr SET total_depance = %s WHERE date = %s''',(o, date))
rowcnt = self.cur.rowcount # number of rows updated - psycopg2
self.connection.commit()
if rowcnt == 0: # no rows updated, need to insert new row
self.cur_t = self.connection.cursor()
self.cur_t.execute( '''INSERT INTO total_jr(total_depance, date)
VALUES (%s, %s)'''
, (o, date))
self.connection.commit()
I find a solution for anyone who need it in future first of all we need to update the table
create_table_total_jr = ''' CREATE TABLE IF NOT EXISTS total_jr (
id SERIAL PRIMARY KEY UNIQUE NOT NULL,
total_revenu DECIMAL(100,2),
total_depance DECIMAL(100,2),
total_différence DECIMAL(100,2),
date DATE UNIQUE)''' #add unique to the date
and after that we use the UPSERT and ON CONFLICT
self.cur_t.execute( ''' INSERT INTO total_jr(date) VALUES (%s)
ON CONFLICT (date) DO NOTHING''', (date,))
self.connection.commit()
with this code when there is an insert value with the same date it will do nothing
after that we update the value of the SUM
self.cur.execute( '''UPDATE total_jr SET total_depance = %s WHERE date = %s''',(o, date))
self.connection.commit()
Special thanks to Mike67 for his help
You do not need 2 database calls for this. As #Mike67 suggested UPSERT functionality is what you want. However, you need to send both date and total_depance. In SQL that becomes:
insert into total_jr(date,total_depance)
values (date_value, total_value
on conflict (date)
do update
set total_depance = excluded.total_depance;
or depending on input total_depance just the transaction value while on the table total_depance is an accumulation:
insert into total_jr(date,total_depance)
values (date_value, total_value
on conflict (date)
do update
set total_depance = total_depance + excluded.total_depance;
I believe your code then becomes something like (assuming the 1st insert is correct)
self.cur_t.execute( ''' INSERT INTO total_jr(date,total_depance) VALUES (%s1,$s2)
ON CONFLICT (date) DO UPDATE set total_depance = excluded.$s2''',(date,total_depance))
self.connection.commit()
But that could off, you will need to verify.
Tip of the day: You should change the column name date to something else. Date is a reserved word in both Postgres and the SQL Standard. It has predefined meanings based on its context. While you may get away with using it as a data name Postgres still has the right to change that at any time without notice, unlikely but still true. If so, then your code (and most code using that/those table(s)) fails, and tracking down why becomes extremely difficult. Basic rule do not use reserved words as data names; using reserved words as data or db object names is a bug just waiting to bite.
I am using pyodbc to fetch total quantity of a product based on a certain criteria called "Strength". The "Strength" column also has string value for some records, so it is a varchar column.
The user enters details like brand, product type, product line, date range, minimum quantity (in this case, 12), and strength range
This is my query:
SELECT SUM(CAST([Qty] AS decimal(10, 2))) AS Qty
FROM (
SELECT
[Brand],
[ProdType],
[Lot],
CAST([Strength] AS DECIMAL(10,4)) AS [Strength],
[ProductLine],
[Size],
[Stage],
[Customer],
[PackedOn],
[Qty],
[RefreshedBy],
[RefreshedOn]
FROM SalesData
WHERE
(isnumeric([Strength]) = 1)
AND [Stage]='WIP'
AND [PackedOn]>='2018-06-03'
AND [PackedOn]<='2020-06-03'
AND [Brand]='ABC'
AND [ProductLine]='DEF'
AND [Size]='15'
AND [Qty]>='12.0'
AND [Strength]>=0.2
AND [Strength]<=0.4
AND [ProdType] Is Null
) as outputdata
This is my table:
ID Brand ProdType Lot Strength ProductLine Size Stage Province PackedOn Qty
1 ABC NULL XXXXXXX 0.16 DEF 15 WIP NULL 2018-12-07 1200
This is the create statement
CREATE TABLE [dbo].[SalesData](
[ID] [int] NOT NULL,
[Brand] [varchar](max) NOT NULL,
[ProdType] [varchar](max) NULL,
[Lot] [varchar](max) NOT NULL,
[Strength] [varchar](max) NOT NULL,
[ProductLine] [varchar](max) NOT NULL,
[Size] [varchar](max) NOT NULL,
[Stage] [varchar](max) NOT NULL,
[Province] [varchar](max) NULL,
[PackedOn] [date] NOT NULL,
[Qty] [float] NOT NULL,
[RefreshedBy] [varchar](max) NULL,
[RefreshedOn] [varchar](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
My problem is that this query results in a Quantity of 1200, even though it is outside the strength range. I am using SQL Server Management Studio V 18.4. How do I solve this?
In your WHERE clause you should use.
TRY_CAST([Strength] AS DECIMAL(10,4))>=0.2 AND TRY_CAST([Strength] AS DECIMAL(10,4))<=0.4
Because sql queries start working from where clauses( and joins) then executes other parts. SELECT is the least important part and if you only use CAST in your select it will be only useful for printing data as your preferred format.
SELECT SUM(CAST([Qty] AS decimal(10, 2))) AS Qty FROM
(SELECT [Brand], [ProdType], [Lot], CAST([Strength] AS DECIMAL(10,4)) AS [Strength], [ProductLine], [Size], [Stage], [Customer], [PackedOn], [Qty], [RefreshedBy], [RefreshedOn]
FROM SalesData
WHERE (isnumeric([Strength]) = 1) AND [Stage]='WIP' AND [PackedOn]>='2018-06-03'
AND [PackedOn]<='2020-06-03' AND [Brand]='ABC' AND [ProductLine]='DEF'
AND [Size]='15' AND [Qty]>='12.0' AND TRY_CAST([Strength] AS DECIMAL(10,4))>=0.2 AND TRY_CAST([Strength] AS DECIMAL(10,4))<=0.4 AND [ProdType] Is Null) as outputdata
You need to CAST() before doing numeric comparison, otherwise SQL Server compares strings, not numbers, which leads to unexpected results: as an example, string-wise, '2' is greater than '12' (since it starts with '2', which is greater than '1') That's true of all numeric comparisons involved in the query (Size is also concerned).
I would suggest TRY_CAST(), which avoids error and returns null if conversion fails (which will effectively fail the condition, and remove the corresponding row from the query).
Also, the subquery is unnecessary.
Consider:
SELECT SUM(Qty) Qty
FROM SalesData
WHERE
Stage = 'WIP'
AND PackedOn >= '2018-06-03'
AND PackedOn <= '2020-06-03'
AND Brand = 'ABC'
AND ProductLine = 'DEF'
AND ProdType Is Null
AND Qty >= 12
AND TRY_CAST(Strength AS DECIMAL(10, 4)) >= 0.2
AND TRY_CAST(Strength AS DECIMAL(10, 4)) <= 0.4
AND TRY_CAST(Size AS INT) = 15
If you want to cast your float output to decimal, it is more accurate to this after the sum(), so:
SELECT CAST(SUM(Qty) AS DECIMAL(10, 2)) Qty
FROM ...
I am working with Python and SQLite. I am constantly getting this message
"near ")": syntax error".
I tried to add a semi-colon to all the queries but still, I get this error message.
tables.append("""
CREATE TABLE IF NOT EXISTS payment (
p_id integer PRIMARY KEY,
o_id integer NON NULL,
FOREIGN KEY(o_id) REFERENCES orders(o_id),
);"""
)
You have a comma before the final closing ). Simply remove it.
i.e. use :-
tables.append("""
CREATE TABLE IF NOT EXISTS payment (
p_id integer PRIMARY KEY,
o_id integer NON NULL,
FOREIGN KEY(o_id) REFERENCES orders(o_id)
);"""
)
Remove the comma in the end of the FOREIGN KEY(o_id) REFERENCES orders(o_id),
The working code will be:
tables.append("""
CREATE TABLE IF NOT EXISTS payment (
p_id integer PRIMARY KEY,
o_id integer NON NULL,
FOREIGN KEY(o_id) REFERENCES orders(o_id)
);"""
)
Try this:
tables = []
tables.append("""
CREATE TABLE IF NOT EXISTS payment p_id integer PRIMARY KEY,
o_id integer NON NULL FOREIGN KEY(o_id) REFERENCES orders(o_id),
""")
print(tables)
I'm trying to create an SQL database with the following fields:
connection= sqlite3.connect('Main Database')
crsr = connection.cursor()
#Creates a table for the teacher data if no table is found on the system
crsr.execute("""CREATE TABLE IF NOT EXISTS Teacher_Table(Teacher_ID INTEGER PRIMARY KEY,
TFirst_Name VARCHAR(25) NOT NULL,
TLast_Name VARCHAR (25) NOT NULL,
Gender CHAR(1) NOT NULL,
Home_Address VARCHAR (50) NOT NULL,
Contact_Number VARCHAR (14) NOT NULL);""")
connection.commit()
connection.close()
But when I input values, the gender field accepts more than one value
Database View
How can I make sure it only accepts one character for that field
How can I make sure it only accepts one character for that field
SQLite does not check the length constraints defined at type level, as is specified in the documentation on types:
(...) Note that numeric arguments in parentheses that following the type name (ex: "VARCHAR(255)") are ignored by SQLite - SQLite does not impose any length restrictions (other than the large global SQLITE_MAX_LENGTH limit) on the length of strings, BLOBs or numeric values.
So you can not enforce this at the database level. You will thus need to enforce this through your views, etc.
We can however, like #Ilja Everilä says, use a CHECK constraint:
CREATE TABLE IF NOT EXISTS Teacher_Table(
Teacher_ID INTEGER PRIMARY KEY,
TFirst_Name VARCHAR(25) NOT NULL,
TLast_Name VARCHAR (25) NOT NULL,
Gender CHAR(1) NOT NULL CHECK (length(Gender) < 2),
Home_Address VARCHAR (50) NOT NULL,
Contact_Number VARCHAR (14) NOT NULL
)
I need to return a list from a table, pairing the rows exactly once; So if I were to have :
ID NAME
01 Jones
02 Clay
03 Mark
04 Nancy
05 Larry
I need to pair them like this:
ID1 NAME1 ID2 NAME2
01 Jones 03 Mark
05 Larry 02 Clay
I can't just pair them in a desc order of the id, as the pairing is based on another table, showing how many times each have won, the problem at the moment is that I get pairs everyone pairing with everyone, how can I limit once a user was mentioned once in a pair, as 1 or 2, it can never be paired again?
As an addon to this question, how can I afterwards do something like :
IF exists without a pair [I do this by adding in another table that a person was pairless at one point] already, then redo query until person without a pair wasn't without a pair already. I know I can easily check results like this in code, but I must do it in psql all (I am using python, if that has anything to do with it);
CREATE TABLE tournaments ( id BIGSERIAL PRIMARY KEY, name VARCHAR(250) );
CREATE TABLE participates (id BIGSERIAL PRIMARY KEY, t_id INT NOT NULL, p_id INT NOT NULL, CONSTRAINT fk_tournament FOREIGN KEY (t_id) REFERENCES tournaments (id), CONSTRAINT fk_player FOREIGN KEY (p_id) REFERENCES players (id));
CREATE TABLE players ( id BIGSERIAL PRIMARY KEY, name VARCHAR(250));
CREATE TABLE matches ( id BIGSERIAL PRIMARY KEY, round INT NOT NULL, p_one_id INT NOT NULL, p_two_id INT, CONSTRAINT fk_p_one_id FOREGN KEY (p_one_id) REFERENCES players (id), CONSTRAINT fk_p_two_id FOREIGN KEY (p_two_id) );
CREATE TABLE wins ( id BIGSERIAL PRIMARY KEY, m_id INT NOT NULL, p_id INT NOT NULL, CONSTRAINT fk_m_id FOREIGN KEY (m_id) REFERENCES matches (id), CONSTRAINT fk_p_id FOREIGN KEY (p_id) REFERENCES players(id) );
Regarding the 'checking if one was already paired', I would check if they have wins with m_id = 0;
I do use psql.