ON CONFLICT statement not executing - postgresql

When conflict value goes to this query, update is not executed (concerning to updated_at field). But insert works perfect with such subquery as SELECT with joins
INSERT INTO history (tx_hash, user_address, address_from, address_to, value, type, status, submitted_at,
coin_id, network_id)
SELECT :tx_hash, :user_address, :address_from, :address_to, :value, :type, :status, :submitted_at,
c.id, n.id
FROM (VALUES (:coin_symbol, :network_symbol) ) x
INNER JOIN coins c on c.symbol = :coin_symbol
INNER JOIN networks n on n.symbol = :network_symbol
ON CONFLICT (tx_hash)
DO UPDATE SET (confirmations, status, updated_at) =
(excluded.confirmations, excluded.status, now());
Table structure gist

Related

Inner join with table that does not contain any record fails?

I have query in which I have applied inner join, it works fine if the joining table on right side has some records, but inner joins return nothing when joining table has 0 (zero) records, and I understand it bcz it has nothing to join on.
I want the records from tbl11 if record has status=1 in tbl11 or if that record exist in tbl12 then it must have status=1, if tbl12.status=0, then that record is not needed.
Table structure
create table tbl11
(
tbl11_id serial primary key,
name character varying,
status integer
)
insert into tbl11(name, status) values('Tony',1),
('Jony',1),
('Sneha',1),
('Aakriti',0)
create table tbl12
(
tbl12_id serial primary key,
tbl11_id integer,
name character varying,
status integer
)
Here is what, I have tried till now
select t1.tbl11_id, t1.name, t1.status from tbl11 t1
inner join tbl12 t2 on t1.tbl11_id = t2.tbl11_id or t1.tbl11_id !=t2.tbl11_id
where t1.status=1 and t2.status=1
It gives no output as tbl12 doesn't have any data.
Then I read about case in postgres and tried this
select case
when
select exists (select 1 from tbl12)
Then
select t1.tbl11_id, t1.name, t1.status from tbl11 t1
inner join tbl12 t2 on t1.tbl11_id = t2.tbl11_id or t1.tbl11_id !=t2.tbl11_id
where t1.status=1 and t2.status=2
else
select tbl11_id, name, status from tbl11 where status=1
But it gives error as
ERROR: syntax error at or near "select"
LINE 3: select exists (select 1 from tbl12)
this is the simple query
select * from tbl11 where status=1 and tbl11_id not in (select tbl11_id from tbl12 where status=0);
DEMO

Postgres join involving tables having join condition defined on an text array

I have two tables in postgresql
One table is of the form
Create table table1(
ID serial PRIMARY KEY,
Type []Text
)
Create table table2(
type text,
sellerID int
)
Now i want to get all the rows from table1 which are having type same that in table2 but the problem is that in table1 the type is an array.
In case the type in the table has an identifiable delimiter like ',' ,';' etc. you can rewrite the query as regexp_split_to_table(type,',') or versions later than 9.5 unnest function can be use too.
For eg.,
select * from
( select id ,regexp_split_to_table(type,',') from table1)table1
inner join
select * from table2
on trim(table1.type) = trim(table2.type)
Another good example can be found - https://www.dbrnd.com/2017/03/postgresql-regexp_split_to_array-to-split-string-using-different-delimiters/
SELECT
a[1] AS DiskInfo
,a[2] AS DiskNumber
,a[3] AS MessageKeyword
FROM (
SELECT regexp_split_to_array('Postgres Disk information , disk 2 , failed', ',')
) AS dt(a)
You can use the ANY operator in the JOIN condition:
select *
from table1 t1
join table2 t2 on t2.type = any (t1.type);
Note that if the types in the table1 match multiple rows in table2, you would get duplicates (from table1) because that's how a join works. Maybe you want an EXISTS condition instead:
select *
from table1 t1
where exists (select *
from table2 t2
where t2.type = any(t1.type));

How to optimise a SQL query to check for consistency of column values across tables

I would like to check across multiple tables that the same keys / same number of keys are present in each of the tables.
Currently I have created a solution that checks the count of keys per individual table, checks the count of keys when all tables are merged together, then compares.
This solution works but I wonder if there is a more optimal solution...
Example solution as it stands:
SELECT COUNT(DISTINCT variable) AS num_ids FROM table_a;
SELECT COUNT(DISTINCT variable) AS num_ids FROM table_b;
SELECT COUNT(DISTINCT variable) AS num_ids FROM table_c;
SELECT COUNT(DISTINCT a.variable) AS num_ids
FROM (SELECT DISTINCT VARIABLE FROM table_a) a
INNER JOIN (SELECT DISTINCT VARIABLE FROM table_b) b ON a.variable = b.variable
INNER JOIN (SELECT DISTINCT VARIABLE FROM table_c) c ON a.variable = c.variable;
UPDATE:
The difficultly that I'm facing putting this together in one query is that any of the tables might not be unique on the VARIABLE that I am looking to check, so I've had to use distinct before merging to avoid expanding the join
Since we are only counting, I think there is no need in joining the tables on the variable column. A UNION should be enough.
We still have to use DISTINCT to ignore/suppress duplicates, which often means extra sort.
An index on variable should help for getting counts for separate tables, but it will not help for getting the count of the combined table.
Here is an example for comparing two tables:
WITH
CTE_A
AS
(
SELECT COUNT(DISTINCT variable) AS CountA
FROM TableA
)
,CTE_B
AS
(
SELECT COUNT(DISTINCT variable) AS CountB
FROM TableB
)
,CTE_AB
AS
(
SELECT COUNT(DISTINCT variable) AS CountAB
FROM
(
SELECT variable
FROM TableA
UNION ALL
-- sic! use ALL here to avoid sort when merging two tables
-- there should be only one distinct sort for the outer `COUNT`
SELECT variable
FROM TableB
) AS AB
)
SELECT
CASE WHEN CountA = CountAB AND CountB = CountAB
THEN 'same' ELSE 'different' END AS ResultAB
FROM
CTE_A
CROSS JOIN CTE_B
CROSS JOIN CTE_AB
;
Three tables:
WITH
CTE_A
AS
(
SELECT COUNT(DISTINCT variable) AS CountA
FROM TableA
)
,CTE_B
AS
(
SELECT COUNT(DISTINCT variable) AS CountB
FROM TableB
)
,CTE_C
AS
(
SELECT COUNT(DISTINCT variable) AS CountC
FROM TableC
)
,CTE_ABC
AS
(
SELECT COUNT(DISTINCT variable) AS CountABC
FROM
(
SELECT variable
FROM TableA
UNION ALL
-- sic! use ALL here to avoid sort when merging two tables
-- there should be only one distinct sort for the outer `COUNT`
SELECT variable
FROM TableB
UNION ALL
-- sic! use ALL here to avoid sort when merging two tables
-- there should be only one distinct sort for the outer `COUNT`
SELECT variable
FROM TableC
) AS AB
)
SELECT
CASE WHEN CountA = CountABC AND CountB = CountABC AND CountC = CountABC
THEN 'same' ELSE 'different' END AS ResultABC
FROM
CTE_A
CROSS JOIN CTE_B
CROSS JOIN CTE_C
CROSS JOIN CTE_ABC
;
I deliberately chose CTE, because as far as I know Postgres materializes CTE and in our case each CTE will have only one row.
Using array_agg with order by is even better variant, if it is available on redshift. You'll still need to use DISTINCT, but you don't have to merge all tables together.
WITH
CTE_A
AS
(
SELECT array_agg(DISTINCT variable ORDER BY variable) AS A
FROM TableA
)
,CTE_B
AS
(
SELECT array_agg(DISTINCT variable ORDER BY variable) AS B
FROM TableB
)
,CTE_C
AS
(
SELECT array_agg(DISTINCT variable ORDER BY variable) AS C
FROM TableC
)
SELECT
CASE WHEN A = B AND B = C
THEN 'same' ELSE 'different' END AS ResultABC
FROM
CTE_A
CROSS JOIN CTE_B
CROSS JOIN CTE_C
;
Well, here is probably the nastiest piece of SQL I could build for you :) I will forever deny that I wrote this and that my stackoverflow account was hacked ;)
SELECT
'All OK'
WHERE
( SELECT COUNT(DISTINCT id) FROM table_a ) = ( SELECT COUNT(DISTINCT id) FROM table_b )
AND ( SELECT COUNT(DISTINCT id) FROM table_b ) = ( SELECT COUNT(DISTINCT id) FROM table_c )
By the way, this won't optimise the query - it's still doing three queries (but I guess it's better than 4?).
UPDATE: In light of your use-case below: NEW sql fiddle http://sqlfiddle.com/#!15/a0403/1
SELECT DISTINCT
tbl_a.a_count,
tbl_b.b_count,
tbl_c.c_count
FROM
( SELECT COUNT(id) a_count, array_agg(id order by id) ids FROM table_a) tbl_a,
( SELECT COUNT(id) b_count, array_agg(id order by id) ids FROM table_b) tbl_b,
( SELECT COUNT(id) c_count, array_agg(id order by id) ids FROM table_c) tbl_c
WHERE
tbl_a.ids = tbl_b.ids
AND tbl_b.ids = tbl_c.ids
The above query will only return if all tables have the same number of rows, ensuring that the IDS are also the same.

Converting Traditional IF EXIST UPDATE ELSE INSERT into MERGE is not working?

I am going to use MERGE to insert or update a table depending upon ehether it's exist or not. This is my query,
declare #t table
(
id int,
name varchar(10)
)
insert into #t values(1,'a')
MERGE INTO #t t1
USING (SELECT id FROM #t WHERE ID = 2) t2 ON (t1.id = t2.id)
WHEN MATCHED THEN
UPDATE SET name = 'd', id = 3
WHEN NOT MATCHED THEN
INSERT (id, name)
VALUES (2, 'b');
select * from #t;
The result is,
id name
1 a
I think it should be,
id name
1 a
2 b
You have your USING part slightly messed up, that's where to put what you want to match against (although in this case you're only using id)
declare #t table
(
id int,
name varchar(10)
)
insert into #t values(1,'a')
MERGE INTO #t t1
USING (SELECT 2, 'b') AS t2 (id, name) ON (t1.id = t2.id)
WHEN MATCHED THEN
UPDATE SET name = 'd', id = 3
WHEN NOT MATCHED THEN
INSERT (id, name)
VALUES (2, 'b');
select * from #t;
As Mikhail pointed out, your query in the USING clause doesn't contain any rows.
If you want to do an upsert, put the new data into the USING clause:
MERGE INTO #t t1
USING (SELECT 2 as id, 'b' as name) t2 ON (t1.id = t2.id) --This no longer has an artificial dependency on #t
WHEN MATCHED THEN
UPDATE SET name = t2.name
WHEN NOT MATCHED THEN
INSERT (id, name)
VALUES (t2.id, t2.name);
This query won't return anything:
SELECT id FROM #t WHERE ID = 2
Because where is no rows in table with ID = 2, so there is nothing to merge into table.
Besides, in MATCHED clause you are updating a field ID on which you are joining table, i think, it's forbidden.
For each DML operations you have to commit (Marks the end of a successful the transaction)Then only you will be able to see the latest data
For example :
GO
BEGIN TRANSACTION;
GO
DELETE FROM HumanResources.JobCandidate
WHERE JobCandidateID = 13;
GO
COMMIT TRANSACTION;
GO

Check if field is null or empty and insert sql server 2008

Bit rusty in sql
I have a situation where I need to insert a field "#Amount" into a temp table.If #amount from tableA is null or 0 get it from tableB
This is a simplified example of what I am doing.This must be done within a select statement when inserting into #CustomerTable
Do I need a case when statement?
DECLARE #Amount DECIMAL(18,4)
SELECT #Amount=Amount
FROM TableA
INSERT #CustomerTable(id,Name,Amount)
SELECT 1,CustomerName,--if Amount is null or 0 get it from TableB else Get it from Table A.
FROM TableB
Since you're using 2008 I'd twist the new NULLIF() function with the ISNULL() function and use a subquery:
insert #CustomTable (id, name, amount)
select
1,
CustomerName,
ISNULL(NULLIF(TableA.Amount,0),(select Amount from TableB where TableB.ID = TableA.ID))
from
TableA
INSERT #CustomerTable(id,Name,Amount)
SELECT 1,
CASE
WHEN Amount IS NULL or Amount = 0 THEN TableA.CustomerName
ELSE TableB.CustomerName
END,
Amount
FROM TableA, TableB
-- need a WHERE clause here to get TableA/TableB records, and you need to make
-- sure you join them properly
Pretty close to #dcp answer, instead using a sub query in the case statement.
INSERT #CustomTable
(
id,
Name,
Amount
)
SELECT
1,
CustomerName,
CASE
WHEN ISNULL(TableB.Amount,0) > 0 THEN TableB.Amount
ELSE (SELECT TableA.Amount FROM TableA WHERE 1 = 1) --Replace logic to get value from TableA
END AS Amount
FROM TableB