Proper syntax for upsert insert update psycopg2 - postgresql

I have a table that I created in postgresql:
> CREATE TABLE issuer(
> cik char(10) NOT null ,issuer_name char(150) NOT NULL ,trading_symbol char(10) NOT
> NULL ,SIC char(6) NOt NULL
> ,date_added timestamp NULL DEFAULT
> CURRENT_TIMESTAMP ,CONSTRAINT issuer_pk PRIMARY key (cik) );
I am trying to either update a row if it exists or insert it if it doesn't.
I have searched the documentation on how to make this work, but I am baffled by the errors I get.
I have a function that I call
io = postgres_update_issuer(con,cur,cik,coname,ticker,'')
When I call this function, python calls threading and then quits.
Here is the function I call:
def postgres_update_issuer(conn,cur,issuer_cik,name,ticker,sic):
sql = """
INSERT INTO issuer ( cik,issuer_name,trading_symbol,SIC)
VALUES (%s,%s,%s,%s)
ON CONFLICT (cik)
DO UPDATE SET
(issuer_name,trading_symbol,SIC )
= (EXCLUDED.issuer_name, EXCLUDED.trading_symbol, EXCLUDED.SIC)
;"""
try:
# data = (issuer_cik,name,ticker,sic)
cur.execute(sql,(issuer_cik,name,ticker,sic) )
return True
except (Exception, psycopg2.DatabaseError) as error:
print(error)
When I change the function to this, I get the couldn't move all fields error message:
def postgres_update_issuer(conn,cur,issuer_cik,name,ticker,sic):
sql = """
INSERT INTO issuer ( cik,issuer_name,trading_symbol,SIC)
VALUES (%s)
ON CONFLICT (cik)
DO UPDATE SET
(issuer_name,trading_symbol,SIC )
= (EXCLUDED.issuer_name, EXCLUDED.trading_symbol, EXCLUDED.SIC)
;"""
try:
data = (issuer_cik,name,ticker,sic)
cur.execute(sql,(data )
return True
except (Exception, psycopg2.DatabaseError) as error:
print(error)
What is the correct way to do this. I am using python 3.6 psycopg2, and postgresql 10

Related

R2DBC using Collection as parameter repository`s method for compare with uuid[]

DataBase: R2DBC Postgres
I have a column model_id at table with type: uuid[].
create table t_job
(
id uuid default gen_random_uuid() not null
primary key,
model_id uuid[] not null,
// -- ANOTHER COLUMN -- //
);
I need compare values from column model_id with Set<UUID>
#Query("""
SELECT case
WHEN COUNT(j) >= 1
THEN true
ELSE false
END
FROM t_job AS j
WHERE j.model_id IN :modelIdSet
AND j.state = 'done'
AND j.output_format = 'COLLISION'
""")
Mono<Boolean> isCollisionJobDoneBySeveralModelsId(String modelIdSet);
OUTPUT:
"debugMessage": "executeMany; bad SQL grammar [ SELECT case\n WHEN COUNT(j) >= 1\n THEN true\n ELSE false\n END\n FROM runner_processing_service.t_job AS j\n WHERE j.model_id IN :modelIdSet\n AND j.state = 'done'\n AND j.output_format = 'COLLISION'\n]; nested exception is io.r2dbc.postgresql.ExceptionFactory$PostgresqlBadGrammarException: [42601] syntax error at or near "$1""
How correct insert and compare values from uuid[] column with Set<UUID>
I try convert Set to String type and give that string to repository`s method, but it is not work to.
This query is work correct from console
enter image description here

sqlalchemy seems have no support for insert cte

By given table creation statement and query it's necessary to get old values before update:
CREATE TABLE IF NOT EXISTS products(
id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,
product_id INT UNIQUE,
image_link CHARACTER VARYING NOT NULL,
additional_image_links CHARACTER VARYING[] NOT NULL
);
WITH temp AS (
INSERT INTO products(product_id, image_link, additional_image_links)
VALUES(1, 'http://www.e1xazm1ple1k113.com',ARRAY['http://www.examkple1113.com','http://www.example2.com'])
ON CONFLICT (product_id) DO UPDATE SET image_link = EXCLUDED.image_link, additional_image_links = EXCLUDED.additional_image_links
WHERE products.image_link != EXCLUDED.image_link OR products.additional_image_links != EXCLUDED.additional_image_links OR products.image_link != EXCLUDED.image_link
RETURNING id, image_link, additional_image_links
)
SELECT image_link, additional_image_links FROM products WHERE id IN (SELECT id FROM temp);
If conflict happens and new values conform criteria result is generated, however I need to use sqlalchemy machinery for it. Approximate but not working example:
def upsert(table, rows, constraint, update_cols):
query = insert(table).values(rows)
return query.on_conflict_do_update(
constraint=constraint,
set_={c: getattr(query.excluded, c) for c in update_cols},
where=getattr(table.c, "additional_image_link") != getattr(query.excluded, "additional_image_link"),
).cte("upsert")
Calling which produces the exception:
sesh = session(autocommit=False, autoflush=False, engine=DEFAULT)
sesh.execute(upsert(*args))
sqlalchemy.exc.ArgumentError: Executable SQL or text() construct expected, got <sqlalchemy.sql.selectable.CTE at 0x1042c3f10; upsert>.

sqlalchemy to create temporary table

I created a temporary table with sqlalchemy (with an underlying postgres database) that is going to be joined with a database table. However, in some cases when a value is empty '' then postgres throws the error:
failed to find conversion function from unknown to text
SqlAlchemy assembles everything to the following context
[SQL: 'WITH temp_table AS \n(SELECT %(param_1)s AS id, %(param_2)s AS email, %(param_3)s AS phone)\n SELECT campaigns_contact.id, campaigns_contact.email, campaigns_contact.phone \nFROM campaigns_contact JOIN temp_table ON temp_table.id = campaigns_contact.id AND temp_table.email = campaigns_contact.email AND temp_table.phone = campaigns_contact.phone'] [parameters: {'param_1': 83, 'param_2': '', 'param_3': '+1234567890'}]
I assemble the temporary table as follows
stmts = []
for row in import_data:
row_values = [literal(row[value]).label(value) for value in values]
stmts.append(select(row_values))
subquery = union_all(*stmts)
subquery = subquery.cte(name="temp_table")
The problem seems to be the part here
...%(param_2)s AS email...
which after replacing the param_2 results in
...'' AS email...
which will cause the error mentioned above.
One way to solve the issue is to perform a cast
...''::text AS email...
However, I don't know how to perform ::text cast with sqlalchemy!?

Postgres function with dblink

Got a problem here with following function in postgresql function:
im getting following error :
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function savegamelog(text,text,integer,integer,bigint,bigint,bigint,integer,integer,integer,integer) line 15 at SQL statement
my function is as follows:
DECLARE
s text;
pDatatime integer;
pDataid bigint;
BEGIN
pDatatime = floor(extract(epoch from '["now()",]'::timestamp));
IF length(pTableName) > 0 THEN
UPDATE lastgamedata SET data= pData::bytea, gameid= pGameid, balance= pBalance ,bet= pBet, win= pWin, betline= pBetline, lines=pLines, datatime= pDatatime WHERE uid= pUserid;
END IF;
UPDATE gamestatistic SET totalin =totalin+pBet , totalout =totalout+pWin WHERE gameindex=pGameid;
SELECT dblink_connect('host=127.0.0.1
user=user
password=pass
dbname=dbname');
SELECT dblink_exec('UPDATE hall SET totalbetin = totalbetin+pBet , totalbetout =totalbetout+pWin WHERE id = (SELECT roomnumber FROM users WHERE uid = pUserid)');
INSERT INTO gamedata( sessionID, uid, gameID, key, balance, bet, win, betline, lines, datatime, type, denomination ) VALUES ( 0, pUserid, pGameid, 0, pBalance, pBet, pWin, pBetline, pLines, pDatatime, pType, pDenomination ) RETURNING dataid INTO pDataid;
INSERT INTO gamedata_storage ( dataid, data ) VALUES ( pDataid, pData::bytea );
return pDataid;
END;
i know the function is wrong, and i dont know how to fix it..
can anybody point me in the correct direction pls..
following the error message instructions, change
SELECT dblink_connect('host=127.0.0.1
user=user
password=pass
dbname=dbname');
to
PERFORM dblink_connect('host=127.0.0.1
user=user
password=pass
dbname=dbname');

Column is of type timestamp without time zone but expression is of type character

I'm trying to insert records on my trying to implement an SCD2 on Redshift
but get an error.
The target table's DDL is
CREATE TABLE ditemp.ts_scd2_test (
id INT
,md5 CHAR(32)
,record_id BIGINT IDENTITY
,from_timestamp TIMESTAMP
,to_timestamp TIMESTAMP
,file_id BIGINT
,party_id BIGINT
)
This is the insert statement:
INSERT
INTO ditemp.TS_SCD2_TEST(id, md5, from_timestamp, to_timestamp)
SELECT TS_SCD2_TEST_STAGING.id
,TS_SCD2_TEST_STAGING.md5
,from_timestamp
,to_timestamp
FROM (
SELECT '20150901 16:34:02' AS from_timestamp
,CASE
WHEN last_record IS NULL
THEN '20150901 16:34:02'
ELSE '39991231 11:11:11.000'
END AS to_timestamp
,CASE
WHEN rownum != 1
AND atom.id IS NOT NULL
THEN 1
WHEN atom.id IS NULL
THEN 1
ELSE 0
END AS transfer
,stage.*
FROM (
SELECT id
FROM ditemp.TS_SCD2_TEST_STAGING
WHERE file_id = 2
GROUP BY id
HAVING count(*) > 1
) AS scd2_count_ge_1
INNER JOIN (
SELECT row_number() OVER (
PARTITION BY id ORDER BY record_id
) AS rownum
,stage.*
FROM ditemp.TS_SCD2_TEST_STAGING AS stage
WHERE file_id IN (2)
) AS stage
ON (scd2_count_ge_1.id = stage.id)
LEFT JOIN (
SELECT max(rownum) AS last_record
,id
FROM (
SELECT row_number() OVER (
PARTITION BY id ORDER BY record_id
) AS rownum
,stage.*
FROM ditemp.TS_SCD2_TEST_STAGING AS stage
)
GROUP BY id
) AS last_record
ON (
stage.id = last_record.id
AND stage.rownum = last_record.last_record
)
LEFT JOIN ditemp.TS_SCD2_TEST AS atom
ON (
stage.id = atom.id
AND stage.md5 = atom.md5
AND atom.to_timestamp > '20150901 16:34:02'
)
) AS TS_SCD2_TEST_STAGING
WHERE transfer = 1
and to short things up, I am trying to insert 20150901 16:34:02 to from_timestamp and 39991231 11:11:11.000 to to_timestamp.
and get
ERROR: 42804: column "from_timestamp" is of type timestamp without time zone but expression is of type character varying
Can anyone please suggest how to solve this issue?
Postgres isn't recognizing 20150901 16:34:02 (your input) as a valid time/date format, so it assumes it's a string.
Use a standard date format instead, preferably ISO-8601. 2015-09-01T16:34:02
SQLFiddle example
Just in case someone ends up here trying to insert into a postgresql a timestamp or a timestampz from a variable in groovy or Java from a prepared statement and getting the same error (as I did), I managed to do it by setting the property stringtype to "unspecified". According to the documentation:
Specify the type to use when binding PreparedStatement parameters set
via setString(). If stringtype is set to VARCHAR (the default), such
parameters will be sent to the server as varchar parameters. If
stringtype is set to unspecified, parameters will be sent to the
server as untyped values, and the server will attempt to infer an
appropriate type. This is useful if you have an existing application
that uses setString() to set parameters that are actually some other
type, such as integers, and you are unable to change the application
to use an appropriate method such as setInt().
Properties props = [user : "user", password: "password",
driver:"org.postgresql.Driver", stringtype:"unspecified"]
def sql = Sql.newInstance("url", props)
With this property set, you can insert a timestamp as a string variable without the error raised in the question title. For instance:
String myTimestamp= Instant.now().toString()
sql.execute("""INSERT INTO MyTable (MyTimestamp) VALUES (?)""",
[myTimestamp.toString()]
This way, the type of the timestamp (from a String) is inferred correctly by postgresql. I hope this helps.
Inside apache-tomcat-9.0.7/conf/server.xml
Add "?stringtype=unspecified" to the end of url address.
For example:
<GlobalNamingResources>
<Resource name="jdbc/??" auth="Container" type="javax.sql.DataSource"
...
url="jdbc:postgresql://127.0.0.1:5432/Local_DB?stringtype=unspecified"/>
</GlobalNamingResources>