SQL Query rounding issue - python

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 ...

Related

How to update a column only when a row has any changes?

I'm using Python to insert JSON data into a PostgreSQL table and I wanted to update a column automatically when a row is updated.
Table definition is:
CREATE TABLE public.customer_data (
id serial4 NOT NULL,
sno int4 NOT NULL,
org public.org NULL,
cust_nbr int8 NULL,
fdc_customer_number int8 NOT NULL,
gender bpchar(1) NULL DEFAULT NULL::bpchar,
mar_status public.mar_status NULL,
spous_name varchar(40) NULL DEFAULT NULL::character varying,
employer varchar(40) NULL DEFAULT NULL::character varying,
designation varchar(30) NULL DEFAULT NULL::character varying,
c_statement_flag public.c_statement_flag NULL,
c_city_code bpchar(2) NULL DEFAULT NULL::bpchar,
c_marital_status public.c_marital_status NULL,
card_vip int4 NULL,
createdon timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedon timestamp NULL,
CONSTRAINT customer_data_pk PRIMARY KEY (fdc_customer_number));
createdon and updatedon columns should have the same timestamp in case of new inserted row. In case of update, only the updatedon column should be updated automatically. How can I achieve this? Or should this be done from Python?.
I use a trigger for this:
CREATE OR REPLACE FUNCTION public.ts_update()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
NEW.updatedon := clock_timestamp();
RETURN NEW;
END;
$function$
customer_data_ts_update BEFORE UPDATE ON public.customer_data FOR EACH ROW EXECUTE FUNCTION ts_update()
This example clock_timestamp() which represents wall clock time. For other choices see Current Date/Time. The plus to the trigger approach is that the field will get set no matter what client is updating the row.

SQL connection problems. ProgrammingError: Incorrect number of bindings supplied. The current statement uses 10, and there are 120 supplied

I have seen there are a lot of posts like this. I have also considered the feedback on the posts but there is a new error regarding incorrect number of bindings.
I created a table on SQL
conn = sqlite3.connect('AQM_2022.db')
c = conn.cursor()
c.execute('''CREATE TABLE Reg2
(CPI,
UNR INT NOT NULL,
M1 INT NOT NULL,
M2 INT NOT NULL,
IMP INT NOT NULL,
EXP INT NOT NULL,
RetailSales INT NOT NULL,
GBBalance INT NOT NULL,
PPI INT NOT NULL,
const INT)''')
print("Table created successfully")*
And i want to export following numbers to my SQL database:
index1=dfGB.index.strftime('%Y-%m-%d %H-%M-%S')
dfGB['Date1']=index1
dfGB.head(5)
I converted it into lists
records_to_insert = dfGB.values.tolist()
records_to_insert
But when i want to export it to SQL:
c = conn.cursor()
c.executemany("INSERT INTO Reg2(CPI,UNR,M1,M2,IMP,EXP,RetailSales,GBBalance,PPI,const) VALUES (?,?,?,?,?,?,?,?,?,?)", [records_to_insert])
conn.commit()
con.close()
The following error pops up:
ProgrammingError: Incorrect number of bindings supplied. The current statement uses 10, and there are 120 supplied.
Does somebody know what the problem could be?
Best regards
You need to provide a list of rows to sqlite3.Cursor.executemany:
You are providing a flat list of 120 values.
Something along the lines of
recs = dfGB.values.tolist()
recs = [recs [v:v+10] for v in range(0,len(recs), 10]
should provide you with a correctly chunked list of list of 10 items each.
If your list goes into million of elements you may want to chunk iteratively instead of creating a new list: How do you split a list into evenly sized chunks?

SQLite Trigger: Update a table after insert is done on another

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.

Restriciting Number of Characters entered into SQLite3

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
)

warning 1292 when I use like in python with mysql

I have to check if there is any null in my database,
and I need to check 11 columns (by or) and plus and like year (ex. 2017%).
def test():
sql= "select date from a000760 where (total_assets is null or total_liabilities is null or sales_figures is null or sales_cost is null or business_profits is null or gross_margin is null or current_income is null or depreciation_expense_of_tangible_assets is null or liquid_asset is null or noncurrent_asset is null or liquid_liability is null) and (date like '2010%')"
curs.execute(sql)
#year="2010"
#curs.execute("select date from a000760 where (total_assets is null or total_liabilities is null or sales_figures is null or sales_cost is null or business_profits is null or gross_margin is null or current_income is null or depreciation_expense_of_tangible_assets is null or liquid_asset is null or noncurrent_asset is null or liquid_liability is null) and (date like %s)",year)
result = curs.fetchall()
if len(result)>0: // print shows () even if it's none. so, I use this
print "a000760"
print "2010 null exists"
It's the test version of one table.
I have to check more than 2000 tables.
It works for this def and shows error (only for one table).
But it doesn't work for total tables.
And I get this error:
Warning: (1292, "Incorrect date value: '2010%' for column 'date' at row 1")
I don't know how...
I've searched for the whole grammar,
but when I type %2017% it doesn't work.
Do not use like with dates! Dates are not strings.
You can just do:
year(date) = 2010
Or:
date >= '2010-01-01' and date < '2011-01-01'

Categories