Python - Passing datetime parameters into a SQL Command

非 Y 不嫁゛ 提交于 2021-02-05 08:10:52

问题


I am trying to do something like this in Python,

SQLCommand = ("Delete From %s where [Date] >= %s and [Date] <= %s", (calendar_table_name, required_starting_date, required_ending_date))

cursor.execute(SQLCommand)

calendar_table_name is a string variable

required_starting_date is a datetime variable

required_ending_date is a datetime variable

Trying this gives me an error:

The first argument to execute must be a string or unicode query.

Tried this and it gives me the same error:

SQLCommand = ("Delete From " +  calendar_table_name + " where [Date] >= %s and [Date] <= %s", ( required_starting_date, required_ending_date))

cursor.execute(SQLCommand)

Edit:

type(required_ending_date)

Out[103]: pandas._libs.tslibs.timestamps.Timestamp


type(required_starting_date)

Out[103]: pandas._libs.tslibs.timestamps.Timestamp

This works in SSMS for me,

  delete from [table_test] where [Date] >= '2007-01-01' and [Date] <= '2021-01-01';

Update :- This is the code, that I am trying with

Delete_SQLCommand =  f"Delete FROM [{calendar_table_name}] WHERE [Date]>=? And [Date]<=?"
params = (required_starting_date, required_ending_date)

required_starting_date & required_ending_date are of "TimeStamp" formats

calendar_tbl_connection = pyodbc.connect(driver=driver, server=required_server, database=database_name,
                     trusted_connection='yes')   
calendar_tbl_cursor = calendar_tbl_connection.cursor()
calendar_tbl_cursor.execute(Delete_SQLCommand,params)
calendar_tbl_connection.commit
calendar_tbl_connection.close()

回答1:


pyodbc has no problem dealing with pandas' Timestamp values as inputs to a proper parameterized query:

# test data
calendar_table_name = "#calendar_table"
crsr.execute(f"CREATE TABLE [{calendar_table_name}] ([Date] date)")
crsr.execute(f"INSERT INTO [{calendar_table_name}] VALUES ('2019-08-22'),('2019-08-24')")
df = pd.DataFrame(
    [(datetime(2019, 8, 23, 0, 0), datetime(2019, 8, 25, 0, 0))],
    columns=['required_starting_date', 'required_ending_date'])
required_starting_date = df.iloc[0][0]
required_ending_date = df.iloc[0][1]
print(type(required_starting_date))  # <class 'pandas._libs.tslibs.timestamps.Timestamp'>

# test
sql = f"DELETE FROM [{calendar_table_name}] WHERE [Date]>=? AND [Date]<=?"
params = (required_starting_date, required_ending_date)
crsr.execute(sql, params)
cnxn.commit()

#verify
rows = crsr.execute(f"SELECT * FROM [{calendar_table_name}]").fetchall()
print(rows)  # [(datetime.date(2019, 8, 22), )]



回答2:


You don't say which library you are using for SQL access, but here is a safe example using psycopg.

from psycopg2 import sql

cmd = sql.SQL("delete from {} where date >= %s and date <= %s")
table_name = sql.Identifier(calendar_table_name)
cur.execute(
    cmd.format(table_name),
    [required_starting_date, required_ending_date]
)

Note that this is not str.format being called, but SQL.format. The library ensures that calendar_table_name is a proper column name, and SQL.format ensures that it is correctly incorporated into your command template before in order to produce a valid parameterized query.


Failing proper library support, you would need to do some sort of dynamic query generation. It should be a restricted sort, though, the more restricted the better. The safest way would be to start with a lookup table of hard-coded queries:

queries = {
  'name1': 'delete from name1 where ... ',
  'name2': 'delete from name2 where ...',
}

This way, you can't construct a query for an arbitrary table name, only select a pre-constructed query.

The second would be to wrap the constructor in a function that checks for a valid table name first. For example,

def generate_query(table_name):
    if table_name not in ['name1', 'name2', ...]:
        raise ValueError("Invalid table name")

    return "delete from {} where ...".format(table_name)



回答3:


You have 3 (at least) different problems with this code:

  1. You are using pandas Timestamp types instead of the expected python datetime types. Roganosh answer explains that
  2. You are mixing sql identifiers (table name) with sql values (date). You can only pass values as parameters to cursor.execute. see chepner's answer.
  3. You are calling cursor.execute with incorrect arguments.

cursor.execute expects two arguments. Since your SQLCommand variable is a tuple, you can use * to unpack the query string and the variables into two arguments when calling cursor.execute.

SQLCommand = (
    "DELETE FROM table_name WHERE date >= %s", 
    (datetime.date(2019, 08, 23),) 
)

cursor.execute(*SQLCommand)

Note that you can't pass sql identfiers such as table names as parameters to the cursor.execute method. The Python Database API Specification does not specify how to construct queries with dynamic identifiers (for example column or table names).



来源:https://stackoverflow.com/questions/57630431/python-passing-datetime-parameters-into-a-sql-command

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!