UPSERT syntax error linked to UPDATE in PostgreSQL (python) - python-3.x

I'm still learning PostgreSQL. During my testing, I have only been using INSERT statement in either psycopg2 and now asyncpg. I now have the need to UPDATE data in my test database, instead of replacing all of it.
I'm currently trying to do a simple replacement test in a testing table, before I move to development table with more data.
I want to replace any $1 name that is in CONFLICT with a name that is already in the table users. I'm trying the query code, which is passed to the DB via asyncpg. I keep getting a syntax errors, so I'm a little lost on how to correct these errors.
What is the proper syntax for this query?
'''INSERT INTO users(name, dob)
VALUES($1, $2)
ON CONFLICT (name)
DO
UPDATE "users"
SET name = 'TEST'
WHERE name = excluded.name '''
UPDATE:
I'm getting this error message when using asyncpg:
asyncpg.exceptions.PostgresSyntaxError: syntax error at or near ""users""
I'm getting this error message when using psycopg2:
psycopg2.ProgrammingError: syntax error at or near ""users""
This is the asyncpg code that I have been using to do the INSERTs:
async def insert_new_records(self, sql_command, data):
print (sql_command)
async with asyncpg.create_pool(**DB_CONN_INFO, command_timeout=60) as pool:
async with pool.acquire() as conn:
try:
stmt = await conn.prepare(sql_command)
async with conn.transaction():
for value in data:
async for item in stmt.cursor(*value):
pass
finally:
await pool.release(conn)
test_sql_command = '''
INSERT INTO users(name, dob)
VALUES($1, $2)
ON CONFLICT (name)
DO
UPDATE "users"
SET name = 'TEST'
WHERE name = excluded.name '''
# The name 'HELLO WORLD' exists in the table, but the other name does not.
params = [('HELLO WORLD', datetime.date(1984, 3, 1)),
('WORLD HELLO', datetime.date(1984, 3, 1))]
loop = asyncio.get_event_loop()
loop.run_until_complete(db.insert_new_records(test_sql_command, params))

You need single quotes around the value for name: SET name='TEST'
The double quotes are for table or column names. In your case, you could just remove the double quotes around users.
After edit:
You should really try your SQL commands in the database console, this has nothing to do with python nor async. It's pure postgresql syntax.
So, the second issue in your query is that you shouldn't specify "users" after UPDATE. It's implied that you're updating the same table. So just DO UPDATE SET... is good.
Then, you'll get column reference "name" is ambiguous. You should write DO UPDATE SET name='TEST'. You already are updating the row where name=excluded.name. I am not 100% clear on what you're trying to do. So if you insert a row once, it's inserted as usual. If you insert it a second time, the name is replaced with 'TEST'. The excluded keyword allows you to access the attempted insert values. So for example, if you wanted to update the last_access column when trying to insert an existing name, you would write ON CONFLICT (name) DO UPDATE last_access=excluded.last_access.

you can test
replace :
'''INSERT INTO users(name, dob)
VALUES($1, $2)
ON CONFLICT (name)
DO
UPDATE "users"
SET name = 'TEST'
WHERE name = excluded.name '''
by :
"""INSERT INTO users(name, dob)
VALUES($1, $2)
ON CONFLICT (name)
DO
UPDATE
SET name = 'TEST'
WHERE name = excluded.name """

Related

Syntax error when using from as a column name in sequelize raw query

I want to save some data to postgres database using sequelize but getting syntax error. One of my column name is 'from'.
let sql = await db.sequelize.query(INSERT INTO chats ( from, msg, chat_room_id) VALUES (${sender},'${msg}',${chat_room_id}));
The syntax error is caused by 'from' column as syntax error at or near "from"
I tried this
let sql = await db.sequelize.query(INSERT INTO chats ( from, msg, chat_room_id) VALUES (${sender},'${msg}',${chat_room_id}));
Expected: Data to be saved into database
What I got: syntax error at or near "from"
If you use reserved PostgreSQL keywords as column names (I don't recommend to do so) then you need to wrap them into "":
let sql = await db.sequelize.query(INSERT INTO chats ( "from", msg, chat_room_id) VALUES (${sender},'${msg}',${chat_room_id}));
The same goes for column names with special characters or with mixed case like chatRoomId.

python3 psycopg SQL identifier must be string

I am trying to reference dynamic tables and fields in a tkinter GUI project using MySQLdb. Using psycopg2.sql to handle an insert statement.
The user select a code, size and color and inputs a quantity. The table names are made up of the size and the code (eg. size-small and code-1111, table_name=small1111). Then the color is the column name and the quantity is an integer entered into the field. The inputs are saved in a dictionary (tdict) when the user selects them. And the dictionary elements are called to be saved in the database table.
table_name = tdict['Size']+tdict['Code']
stmnt = ("INSERT INTO {} (%s, Date) VALUES(%s, %s)").format(sql.Identifier((table_name, tdict['Color'])))
c.execute(sql.SQL(stmnt, (tdict['Quantity'], date)))
The insert query is giving me a TypeError
TypeError("SQL identifiers must be strings")
Can anyone please help? What am I doing wrong? How should the Identifier be made to behave as a string?
Note: I've tried to pass the Identifier elements through a str class but it didn't work. ie
stmnt = ("INSERT INTO {} (%s, Date) VALUES(%s, %s)").format(sql.Identifier((str(table_name, tdict['Color']))))
You are asking sql.Identifier() to create an identifier out of a tuple, e.g. ('small1111','magenta'). Because format() only substitutes into braces {} (and not %s), I think what you actually had in mind was this:
stmnt = sql.SQL("INSERT INTO {} ({}, Date) VALUES(%s %s)").format( sql.Identifier(table_name), sql.Identifier(tdict['Color']) )
I'd suggest you rethink your database design, though --- you should probably have columns named size, code, and color rather than separate tables and columns for each. That will prevent you from having to add a new column each time a new color or a new table for each new size or code. SELECT count(*) FROM inventory WHERE size = 'small' AND code = '1111' GROUP BY color seems preferable to having to create queries dynamically.
This error message will also appear when you have a typo error where you should have used sql.Literal('someFixedNumber'), but instead using sq.Identifier('someFixedNumber')

How to return the Primary Key of the newly inserted row using asyncpg?

I wanted to do something like this:
async with app.pg_pool.acquire() as pg:
uid = await pg.execute('INSERT INTO users (created, keyed, key, email) '
'VALUES ($1, $2, $3, $4) RETURNING id',
time, time, key, rj['email'])['id']
However Connection.execute doesn't seem to return anything other than the status:
https://magicstack.github.io/asyncpg/current/api/index.html?highlight=returning#asyncpg.connection.Connection.execute
The question could be otherwise phrased as: How do I get back the response of the RETURNING statement when using asyncpg?
Simply use Connection.fetchval() instead of execute().
The Connection.execute will return the status of the INSERT. If you need the Record, Records or Value use one of the following instead of Connection.execute :
Connection.fetchval - returns only the value of one field specified. Returns null if no fields are specified.
Connection.fetch - will return an array of dict. Each dict will have the list of fields specified after the RETURNING statement.
Connection.fetchrow - will return a dict with the list of fields specified after the RETURNING statement

Inserting data into database with python/sqlite3 by recognising the column name

I've got a problem that I don't know how to solve, I've tried many solutions but always getting that Operational error: near...
def insert_medicine_to_table():
con = sqlite3.connect('med_db3.db')
cur = con.cursor()
table_name = 'medicines'
column_name = "présentation"
value = 'Boîte de 2 seringues pré-remplies'
cur.execute("INSERT INTO medicines {} VALUES (?)".format(column_name), value)
con.commit()
con.close()
sqlite3.OperationalError: near "présentation": syntax error
The goal here is that either the script or python has to recognize the field (column name) and insert the value into "that" field, like the following:
fields = ['présentation', 'princeps', 'distributeur_ou_fabriquant', 'composition', 'famille', 'code_atc', 'ppv', 'prix_hospitalier', 'remboursement', 'base_de_remboursement__ppv', 'nature_du_produit']
values = ['Boîte de 2 seringues pré-remplies', 'Oui', 'SANOFI', 'Héparine', 'Anticoagulant héparinique', 'B01AB01', '43.80', '27.40', 'Oui', '43.80', 'Médicament']
That is one entry in the database. The problem here is that other entries can or not have one or more values for some field, and also the fields are not presented in the same order in other entries.
It has to recognize each field in the database table and insert each value into the right column.
The problem causing your error is that your SQL isn't valid. The statement you are trying to execute is:
INSERT INTO medicines présentation VALUES (?)
The statement you want to execute is:
INSERT INTO medicines ("présentation") VALUES (?)
As far as your larger question is concerned, if you create both the list of columns ("présentation") and list of parameter markers (?) and build the query using them, you're most of the way there.
If a field can have multiple values supplied for each "entry" in your database, you may need to change your database design to handle that. You'll at least need to figure out how you want to handle the situation, but that would be a matter for a different question.

How to get last insert id when using sub query in Kohana

How do I return the correct $insert_id when using a sub query in Kohana?
I'm using the query method to return $insert_id and $affected_rows. It returns the correct value for $affected_rows but returns '1' for $insert_id which is incorrect.
Query below:
$sub = DB::select('id', 'username', 'email', 'lastVisitDate')->from('jos_users');
$qry_migrate_users = DB::insert('temp_users', array('old_id', 'username', 'email_work', 'last_login'))->select($sub);
list($insert_id, $affected_rows) = $qry_migrate_users->execute($this->conn_target);
MySQL returns only last insert id and affected rows. there is only one way to do what you want - execute your sub-select to array, and using foreach do single inserts. But it's a lilte bit slower operation! Or after inserting do something like that:
SELECT id FROM temp_users WHERE email IN (select email from jos_users)
You might understand the logic

Resources