Postgres CTE Insert and get count - postgresql

Here's a CTE query. After the insert, I want to get the updated count. The insert happens fine but the count returns the count before the INSERT and does not include the new row. Can you please let me know if I am doing something wrong here?
WITH reply_data(id, threadid, commentid, userid, description, created, updated) AS (
VALUES ('27c12e17-b105-48fd-897b-82e5965ab15a'::uuid,
'bbe04e77-0e53-4716-b001-81e7dbf40d70'::uuid,
'fd2513fb-5e92-4a40-a295-6c122c325166'::uuid,
'5b3a6120-233e-4b77-9160-c08c484db31b'::uuid,
'Manual Reply to comment from SQL',
now(),
now())
),
reply_insert AS (
INSERT INTO replies (id, threadid, commentid, userid, description, created, updated)
SELECT rd.id, rd.threadid, rd.commentid, rd.userid, rd.description, rd.created, rd.updated
FROM reply_data rd
RETURNING id, commentid
),
user_reply_insert as (
INSERT INTO user_replies (userid, replyid)
SELECT rd.userid, rd.id FROM reply_data rd
RETURNING userid
),
replyCount as (
select count(*) as repliescount
from replies r,
reply_data rd
where r.commentid = rd.commentid
)
SELECT repliescount FROM replyCount;

According to Postgres document all sub-statements of a query with CTEs happen virtually at the same time. I.e., they are based on the same snapshot of the database.
You would need two statements (in a single transaction) for what you are trying to do OR calculate with total data when inserted in CTE:
WITH reply_data(id, threadid, commentid, userid, description, created, updated) AS (
VALUES ('27c12e17-b105-48fd-897b-82e5965ab15a'::uuid,
'bbe04e77-0e53-4716-b001-81e7dbf40d70'::uuid,
'fd2513fb-5e92-4a40-a295-6c122c325166'::uuid,
'5b3a6120-233e-4b77-9160-c08c484db31b'::uuid,
'Manual Reply to comment from SQL',
now(),
now())
),
reply_insert AS (
INSERT INTO replies (id, threadid, commentid, userid, description, created, updated)
SELECT rd.id, rd.threadid, rd.commentid, rd.userid, rd.description, rd.created, rd.updated
FROM reply_data rd
RETURNING id, commentid
),
user_reply_insert as (
INSERT INTO user_replies (userid, replyid)
SELECT rd.userid, rd.id FROM reply_data rd
RETURNING userid
),
replyCount as (
select count(*) + (select count(*) from reply_insert) as repliescount
from replies r,
reply_data rd
where r.commentid = rd.commentid
)
SELECT repliescount FROM replyCount;

Related

postgres how to insert values with 2 selects

I'm trying to do a query on Postgres but it's not working. I'd like to create an insert query with 2 select:
Example :
INSERT INTO table1 (id_1, id_2)
SELECT id from table_2 where code='01',
SELECT id from table_2 where code='02';
I don't find the good syntax for this.
I believe below query will works for your use case
INSERT INTO stats(totalProduct, totalCustomer, totalOrder)
VALUES(
(SELECT COUNT(*) FROM products),
(SELECT COUNT(*) FROM customers),
(SELECT COUNT(*) FROM orders)
);
you can changes query accordingly
You can add one more SELECT to achieve this
INSERT INTO table_1 (id_1, id_2)
SELECT
(SELECT id FROM table_2 WHERE code = '01') AS Id_1,
(SELECT id FROM table_2 WHERE code = '02') AS Id_2;
Or you may try with CASE expression:
INSERT INTO table1 (id_1, id_2)
SELECT MAX(CASE WHEN code = '01' THEN id ELSE 0 END) AS Id_1,
MAX(CASE WHEN code = '02' THEN id ELSE 0 END) AS Id_2
FROM table_2
Please refer to the working fiddle on db<>fiddle

How to order by result set in union query in T-SQL

I want to use order by clause in my last sql query and I have more than 3 union queries. I do not want to order the top 2 union query but I want to use order by clause in my last sql statement.
Currently, getting error
ORDER BY items must appear in the select list if the statement contains a UNION, INTERSECT or EXCEPT operator.
select 'Total Number of pat' Name, convert(varchar(20), count(id)) Number from table2 where id = 5
union
select 'Total Number of Doc' Name, convert(varchar(20), count(id)) Number from table3
union
select x.usertype, count(distinct userid) cnt
from [dbo].table1 t
cross apply (values (
case when t.userid like '%[0-9][0-9[0-9]' then 'transition' else 'non transition' end,
t.userid
)) x(usertype, userid)
where t.date >= dateadd(day,-7, getdate())
group by x.usertype
order by usertype desc
order by is sorting the result of the unions all together however you can introduce a orderIndex column for imlementing the right ordering.
Here the sample:
I've tried to build sample data in the following code.
create table table1(
userid varchar(100),
usertype varchar(100),
date date
)
insert into table1(userid, date) values ('Einsmayr', '2020-10-27')
insert into table1(userid, date) values ('Eins123', '2020-10-27')
insert into table1(userid, date) values ('Einschmid', '2020-10-27')
insert into table1(userid, date) values ('Einshuber', '2020-10-27')
insert into table1(userid, date) values ('Einsreitmayr', '2020-10-27')
create table table2 (
Name varchar(100),
id int
)
insert into table2(Name, id) values('Zweirich', 5)
insert into table2(Name, id) values('Zweifel', 6)
create table table3 (
Name varchar(100),
id int
)
insert into table3(Name, id) values('Dreisinger', 17)
insert into table3(Name, id) values('Dreibert', 18)
This allows the following queries:
select usertype, Number
from (
select 'Total Number of pat' usertype, convert(varchar(20), count(id)) Number, 1 orderIndex from table2 where id = 5
union
select 'Total Number of Doc' Name, convert(varchar(20), count(id)) Number, 2 orderIndex from table3
union
select usertype, count(distinct userid) Number, 3 orderIndex
from (
select userid, case when userid like '%[0-9][0-9[0-9]' then 'transition' else 'non transition' end usertype
from table1
where date >= dateadd(day,-7, getdate())
) x
group by x.usertype
) y
order by y.orderIndex, y.usertype
Find the solution here: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=ac396c48f5dbcb4a53ad40fac70e9236

How to create temporary tables inside a postgres function

The problem here is that by the time i go to the UPDATE block of code, i no longer have access to the data that was in subquery
I tried many variations of creating a temporary table and a select into from deleted_rows instead of the subquery AS part of the WITH statement but it did not like anything i tried, and it especially didn't like me trying to create a table after the initial with clause
CREATE OR REPLACE FUNCTION public.aggregate_userviews(
)
RETURNS text
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$BEGIN
WITH deleted_rows AS (
DELETE FROM user_details_views
WHERE ts < (timezone('UTC', now() - interval '5 minutes')) RETURNING *
), subquery AS (SELECT DISTINCT username, DATE(ts) as day_of_month, COUNT(id) AS user_views
FROM deleted_rows
GROUP BY username, day_of_month
ORDER BY day_of_month ASC)
INSERT INTO analytics_summary ( username, day_of_month, user_views)
SELECT username, day_of_month, user_views
FROM subquery
ON CONFLICT (username ,day_of_month)
DO UPDATE SET user_views = analytics_summary.user_views + excluded.user_views;
UPDATE user_details u
SET view_count = u.view_count + subquery.user_views
FROM subquery
WHERE u.username=subquery.username;
RETURN NULL;
END;$BODY$;
If i remove the update statement it works perfectly, and i could probably use a trigger to do the update but i would rather not if i am not far off from a solution with what i have
Got it, i had to create the table above the WITH and then fill it before the first insert and then use the temp table for the following two blocks of code like
CREATE OR REPLACE FUNCTION public.aggregate_userviews(
)
RETURNS text
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$BEGIN
create temporary table temp_userviews_table (username varchar, day_of_month date, user_views int);
WITH deleted_rows AS (
DELETE FROM user_details_views
WHERE ts < (timezone('UTC', now() - interval '5 minutes')) RETURNING *
), subquery AS (SELECT DISTINCT username, DATE(ts) as day_of_month, COUNT(id) AS user_views
FROM deleted_rows
GROUP BY username, day_of_month
ORDER BY day_of_month ASC)
INSERT INTO temp_userviews_table (username, day_of_month, user_views)
SELECT username, day_of_month, user_views
FROM subquery;
INSERT INTO analytics_summary ( username, day_of_month, user_views)
SELECT username, day_of_month, user_views
FROM temp_userviews_table
ON CONFLICT (username ,day_of_month)
DO UPDATE SET user_views = analytics_summary.user_views + excluded.user_views;
UPDATE user_details u
SET view_count = u.view_count + temp_userviews_table.user_views
FROM temp_userviews_table
WHERE u.username=temp_userviews_table.username;
drop table temp_userviews_table;
RETURN NULL;
END;$BODY$;

Postgresql Multiple insertions into any tables depending on result of one select

Using Postgresql (9.6) i need to execute multiple insert queries into any tables (table1, table2, table3, ...) depending on result of one select query from another tableMain if result has one or more records, like:
{
insert into table1 (id, name) values(1, 'name');
insert into table2 (id, name) values(1, 'name');
insert into table3 (id, name) values(1, 'name');
} if exists (select id from tableMain where id = 1)
You can use a data modifying CTE that first checks if the row in tablemain exists, and then re-uses that result in subsequent INSERT statements.
with idcheck (main_exist) as (
select exists (select * from tablemain where id = 1 limit 1)
), t1 as (
insert into table1 (id, name)
select 1, 'name'
from idcheck
where main_exists
), t2 as (
insert into table2 (id, name)
select 1, 'name'
from idcheck
where main_exists
)
insert into table3 (id, name)
select 1, 'name'
from idcheck
where main_exists;
If you always want to insert the same values in all three tables, you can include those values in the first query so that you don't need to repeat them:
with idcheck (id, name, main_exist) as (
select 1,
'name',
exists (select * from tablemain where id = 1 limit 1)
), t1 as (
insert into table1 (id, name)
select id, name
from idcheck
where main_exists
), t2 as (
insert into table2 (id, name)
select id, name
from idcheck
where main_exists
)
insert into table3 (id, name)
select id, name
from idcheck
where main_exists;

What's wrong with this T-SQL MERGE statement?

I am new to MERGE, and I'm sure I have some error in my code.
This code will run and create my scenario:
I have two tables, one that is called TempUpsert that fills from a SqlBulkCopy operation (100s of millions of records) and a Sales table that holds the production data which is to be indexed and used.
I wish to merge the TempUpsert table with the Sales one
I am obviously doing something wrong as it fails with even the smallest example
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TempUpsert]') )
drop table TempUpsert;
CREATE TABLE [dbo].[TempUpsert](
[FirstName] [varchar](200) NOT NULL,
[LastName] [varchar](200) NOT NULL,
[Score] [int] NOT NULL
) ON [PRIMARY] ;
CREATE TABLE [dbo].[Sales](
[FullName] [varchar](200) NOT NULL,
[LastName] [varchar](200) NOT NULL,
[FirstName] [varchar](200) NOT NULL,
[lastUpdated] [date] NOT NULL,
CONSTRAINT [PK_Sales] PRIMARY KEY CLUSTERED
(
[FullName] ASC
)
---- PROC
CREATE PROCEDURE [dbo].[sp_MoveFromTempUpsert_to_Sales]
(#HashMod int)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
MERGE Sales AS trget
USING (
SELECT
--- Edit: Thanks to Mikal added DISTINCT
DISTINCT
FirstName, LastName , [Score], LastName+'.'+FirstName AS FullName
FROM TempUpsert AS ups) AS src (FirstName, LastName, [Score], FullName)
ON
(
src.[Score] = #hashMod
AND
trget.FullName=src.FullName
)
WHEN MATCHED
THEN
UPDATE SET trget.lastUpdated = GetDate()
WHEN NOT MATCHED
THEN INSERT ([FullName], [LastName], [FirstName], [lastUpdated])
VALUES (FullName, src.LastName, src.FirstName, GetDate())
OUTPUT $action, Inserted.*, Deleted.* ;
--print ##rowcount
END
GO
--- Insert dummie data
INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Smith',2);
INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Block',2);
INSERT INTO TempUpsert (FirstName, LastName, Score)
VALUES ('John','Smith',2); --make multiple on purpose
----- EXECUTE PROC
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[sp_MoveFromTempUpsert_to_Sales]
#HashMod = 2
SELECT 'Return Value' = #return_value
GO
This returns:
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
Msg 2627, Level 14, State 1, Procedure sp_MoveFromTempUpsert_to_Sales, Line 12
Violation of PRIMARY KEY constraint 'PK_Sales'. Cannot insert duplicate key in object
'dbo.Sales'. The statement has been terminated.
(1 row(s) affected)
What am I doing wrong please?
Greatly appreciated
The first two rows in your staging table will give you the duplicate PK. violation. Conc is the PK and you insert tmain+dmain with the same value twice.
In Summation
MERGE requires its input (Using) to be duplicates free
the Using is a regular SQL statement, so you can use Group By, distinct and having as well as Where clauses.
My final Merge looks like so :
MERGE Sales AS trget
USING (
SELECT FirstName, LastName, Score, LastName + '.' + FirstName AS FullName
FROM TempUpsert AS ups
WHERE Score = #hashMod
GROUP BY FirstName, LastName, Score, LastName + '.' + FirstName
) AS src (FirstName, LastName, [Score], FullName)
ON
(
-- src.[Score] = #hashMod
--AND
trget.FullName=src.FullName
)
WHEN MATCHED
THEN
UPDATE SET trget.lastUpdated = GetDate()
WHEN NOT MATCHED
THEN INSERT ([FullName], [LastName], [FirstName], [lastUpdated])
VALUES (FullName, src.LastName, src.FirstName, GetDate())
OUTPUT $action, Inserted.*, Deleted.* ;
--print ##rowcount
END
And it works!
Thanks to you all :)
Without DISTINCT or proper AGGREGATE function in subquery used in USING part of MERGE there will be two rows which suits criteria used in ON part of MERGE, which is not allowed. (Two John.Smith)
AND
Move the condition src.[Score] = #hashMod inside the subquery,
instead if ON clause not succeed, for example John.Smith have score of 2, and #HashMod = 1 - then if you already have the row with John.Smith in target table - you'll get an error with Primary Key Constraint