Rigth way for this CTE in Postgresql - sql

My goal is to pickup the id in the Soggetto entry and fill it into tipo and tipo2.I can't figure out how to write in the right way this CTE in Postgres via pg in nodejs. I'm little bit confused.
var query = client.query('WITH retid AS (INSERT INTO "Soggetto" (nome, cognome, "regSociale", "partIVA") VALUES ($1, $2, $3, $4) RETURNING id ), INSERT INTO "Tipologia" (privato, azienda) VALUES ($5, $6) SELECT id FROM retid, INSERT INTO "Tipologia2" (cliente, fornitore) VALUES ($7, $8) SELECT id FROM retid ',
[sog.nome, sog.cognome, sog.ragioneSociale, sog.partitaIva, sog.cliente, sog.fornitore, sog.privato, sog.azienda], function(err, result){
if(err){
console.log(err);
}else{
res.writeHead(200, {'content-type': 'text/plain'});
res.end('Insert ok');
}
});

The syntax is not correct. It is unclear exactly what you want, but each CTE needs its own name. So, something like this:
WITH retid AS (
INSERT INTO "Soggetto" (nome, cognome, "regSociale", "partIVA")
VALUES ($1, $2, $3, $4)
RETURNING id
),
t as (
INSERT INTO "Tipologia" (privato, azienda)
VALUES ($5, $6)
),
t2 as (
INSERT INTO "Tipologia2" (cliente, fornitore)
VALUES ($7, $8)
)
SELECT id
FROM retid;

Related

Subquery / embedded multiple joins?

Firstly apologies if this is a duplicate - I'm not quite sure what the correct terminology is for what I'm trying to achieve, so may have missed an implementation in other posts.
Need a query which acting on a single table (being called through ODBC to either MySQL or Oracle) that mimics a file based system.
Logic is to return the latest version of an item. The latest is defined over a composite key of VERSION and DRAFT.
pseudo logic is
for each document (
find highest VERSION
for each VERSION (
find highest DRAFT (
Add item to list
)
)
)
)
I would like to know if there's a way to achieve this with one query with embedded self selects or joins or (sorry this is where the terminology bit breaks down)
I have attempted:
CREATE TABLE docs
(
TYPE varchar(20) NOT NULL,
ID VARCHAR(20) NOT NULL,
VERSION VARCHAR(20) NOT NULL,
DRAFT VARCHAR(20) NOT NULL
);
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc1', ' ', 'A') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc1', ' ', 'B') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc1', '1', ' ') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc1', '1', 'A') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc1', '2', ' ') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc2', ' ', 'A') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc2', ' ', 'B') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc2', '1', ' ') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc2', '1', 'A') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc2', '2', ' ') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc2', '2', 'A') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc3', ' ', 'A') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc4', ' ', 'A') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('REQ','doc4', '1', ' ') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('TEST','doc1', ' ', 'A') ;
INSERT INTO docs (TYPE, ID, VERSION, DRAFT) VALUES ('TEST','doc1', '1', ' ') ;
select * from docs items
inner join ((select ID as vID, max(VERSION) as ver from docs group by ID) vers
inner join (select ID as dID, VERSION as dver, max(DRAFT) as dft from docs group by ID, VERSION) drfts
on (vers.vID = drfts.dID and vers.ver = drfts.dver))
on items.ID = vers.vID
where TYPE='REQ' order by ID;
But I do not get the results expected. Any guidance as to what topic / method I should investigate would be appreciated.
Edit: response to comment
Desired output:
doc1,2,' '
doc2,2,'a'
doc3,' ','a'
doc4,1,' '
Logic is to return the latest version of an item. The latest is defined over a composite key of VERSION and DRAFT.
Use window functions. The following returns one row per document, the row with the highest version and then for that version, the highest draft:
select d.*
from (select d.*,
row_number() over (partition by id order by version desc, draft desc) as seqnum
from docs d
) d
where seqnum = 1;

How to vary result of LISTAGG() depending on number of aggregated elements in Oracle 11g+?

How to print different output within LISTAGG() depending on number of aggregated elements?
Is it possible to get number of aggreated elements without additional COUNT(*) query?
There is an example DDL:
create table shepherds (
SHEPHERD_ID NUMBER(19),
SHEPHERD_NAME VARCHAR2(50 CHAR)
);
create table sheeps (
SHEEP_ID VARCHAR2(10 CHAR),
SHEEP_NAME VARCHAR2(50 CHAR),
SHEEP_SHEPHERD_ID NUMBER(19)
);
-- insert shepherds
insert into shepherds VALUES (111, 'Asher');
insert into shepherds VALUES (222, 'Joseph');
insert into shepherds VALUES (333, 'Nicodemus');
-- first shepherd (one sheep)
insert into sheeps VALUES ('A', 'Mark', 111);
-- second shepherd (two sheeps)
insert into sheeps VALUES ('A', 'Andres', 222);
insert into sheeps VALUES ('B', 'Jeffrey', 222);
-- third shepherd (three sheeps)
insert into sheeps VALUES ('B', 'Jeffrey', 333);
insert into sheeps VALUES ('A', 'Andres', 333);
insert into sheeps VALUES ('D', 'Andres', 333);
Now I want to display all shepherds with new-line separated sheep names in the following way:
SELECT
SHEPHERD_NAME,
(SELECT
listagg(SHEEP_ID || ': ' || SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY SHEEP_ID)
FROM SHEEPS
WHERE SHEEP_SHEPHERD_ID = SHEPHERD_ID)
FROM SHEPHERDS;
The result is: http://sqlfiddle.com/#!4/881a7/3
However, I want to hide sheep's ID letter for those shepherds who have only one sheep.
I tried the following:
SELECT
SHEPHERD_NAME,
(SELECT
listagg(
CASE WHEN COUNT(*) > 1 THEN SHEEP_ID || ': ' ELSE '' END
|| SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY SHEEP_ID)
FROM SHEEPS
WHERE SHEEP_SHEPHERD_ID = SHEPHERD_ID)
FROM SHEPHERDS;
However, I get error:
ORA-00978: nested group function without GROUP BY
http://sqlfiddle.com/#!4/881a7/7
Is it possible to return different string from LISTAGG() if there is only one element to aggregate?
How to detect number of aggregated elements without slowing down query performance in Oracle 11g or higher?
A conditional expression in the subquery should do what you want:
SELECT sh.SHEPHERD_NAME,
(SELECT (CASE WHEN COUNT(*) = 1 THEN MAX(s.SHEEP_NAME)
ELSE LISTAGG(s.SHEEP_ID || ': ' || s.SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY s.SHEEP_ID)
END) as SHEEPS
FROM SHEEPS s
WHERE s.SHEEP_SHEPHERD_ID = sh.SHEPHERD_ID
) as SHEEPS
FROM SHEPHERDS sh;
Here is a db<>fiddle.
The solution without a subquery use a simple GROUP BY, COUNT(*) = 1 to distinct the sheep count and two different LISTAGG statements
SELECT
s.SHEPHERD_NAME,
case when count(*) = 1 then
listagg(SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY SHEEP_ID)
else
listagg(SHEEP_ID || ': ' || SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY SHEEP_ID) end as SHEEPS
FROM SHEPHERDS s
JOIN SHEEPS sh on s.SHEPHERD_ID = sh.SHEEP_SHEPHERD_ID
GROUP BY s.SHEPHERD_NAME /* add SHEPHERD_ID in GROUP BY if the name is not unique */
returns
SHEPHERD_NAME, SHEEPS
Asher Mark
Joseph A: Andres
B: Jeffrey
Nicodemus A: Andres
B: Jeffrey
D: Andres

INSERT in CASE expression from subselect PostgreSQL

I am trying to create a query like this
WITH insert1 as (),
...
subselect1 as (SELECT
(CASE
WHEN %s is NOT NULL THEN
(INSERT INTO Duration (duration)
VALUES (ROW (%s, %s)) RETURNING id as id_duration)
ELSE
(INSERT INTO Distance (length)
VALUES (ROW (%s, %s)) RETURNING id as id_distance)
END)),
...
INSERT INTO FinalTable...
I'm having trouble with the syntax, I know. Do you accomplish this by with Insert into?
My plan is:
By one WITH statement make several insertions with returning values and finally insert to the FinalTable. Having only INSERT and RETURNING values it works great - I have to refer them in FinalTable e.g. (SELECT id_point from insert3).
But this case - I would like to return value from insert, wrapped in CASE (%s means parametrized query, variables passed from python). So in case first %s is NOT NULL, I have to insert to table Duration, else I have to insert to table Distance.
When inserting in FinalTable, I have references to these tables (columns idDistance, idDuration) - so I would like to write smth like (..., (SELECT id_duration from subselect1), (SELECT id_distance from subselect1)...)
What's wrong with my syntax?
It's very unclear to me what you are trying to achieve, but maybe you are looking for something like this:
WITH insert1 as (
...
),
insert_duration (id_duration) as (
insert into duration (duration, ....)
select ....
from (
values (..),(..)
) as t(...)
where $1 IS NOT NULL --<< first branch of your CASE expression
returning id
),
insert_distance (id_distance) as (
insert into distance (length, ...)
select ....
from (
values (..),(..)
) as t(...)
where $1 IS NULL --<< ELSE branch of your CASE expression
returning id
)
INSERT INTO final_table
...

What is the macros of multiple values for INSERT?

INSERT INTO reports (id, date, message)
VALUES ($1, $2, $3)
RETURNING id
Is it possible to replace ($1, $2, $3) with something simpler, like ? or *? It's a bit frustrating - to type in all values.
You could use INSERT INTO SELECT FROM VALUES() syntax:
INSERT INTO reports (id, date, message)
SELECT col1, col2, 'const_value'
FROM (VALUES (...,...),
(...,...)) s(col1, col2, ...,coln)
RETURNING id;
Sample demo:
CREATE TABLE reports(id SERIAL, daten DATE, message TEXT);
INSERT INTO reports ( daten, message)
SELECT col2, 'const_value'
FROM (VALUES ('2019-01-01'::date),
('2019-01-02'::date)) s(col2)
RETURNING id;
SELECT * FROM reports;
db<>fiddle demo

Aggregation in UPDATE SQL query

So, I have currently have two tables, one containing the fixed part of each object, and the other containing a variable number of properties of each object as key/value pairs. I would like to convert these properties to a CLOB field in the main table in JSON map format.
create table test_a (id integer, properties clob);
create table test_b (id integer, a_id integer, key char(30), value char(30));
insert into test_a values(1,'');
insert into test_a values(2,'');
insert into test_a values(3,'');
insert into test_b values (1, 1, 'k1', 'v1');
insert into test_b values (2, 1, 'k2', 'v2');
insert into test_b values (3, 2, 'k3', 'v3');
insert into test_b values (4, 2, 'k4', 'v4');
insert into test_b values (5, 2, 'k5', 'v5');
insert into test_b values (6, 2, 'k6', 'v6');
I can build the JSON I want via the following query:
WITH PROPS AS
(SELECT '"'
||trim(KEY)
||'":"'
||trim(value)
||'"' json,
test_b.*
FROM test_b
)
SELECT test_a.id,
'{'
||
(SELECT listagg(json, ',') within GROUP (
ORDER BY props.key)
FROM PROPS
WHERE PROPS.A_ID = test_a.id
)
|| '}'
FROM TEST_A
INNER JOIN PROPS
ON TEST_A.ID = PROPS.A_ID
GROUP BY TEST_A.ID ;
And I get the desired result
1 "{""k1"":""v1"",""k2"":""v2""}"
2 "{""k3"":""v3"",""k4"":""v4"",""k5"":""v5"",""k6"":""v6""}"
But when I try to use this result to insert into the master table, I get errors
UPDATE TEST_A SET PROPERTIES = (
WITH PROPS AS
(SELECT '"'
||trim(key)
||'":"'
||trim(value)
||'"' json,
test_b.*
FROM test_b
)
SELECT '{'
||
(SELECT listagg(json, ',') within GROUP (order by props.key) FROM PROPS
WHERE PROPS.A_ID = test_a.id)
|| '}' from TEST_A INNER JOIN PROPS ON TEST_A.ID = PROPS.A_ID GROUP BY TEST_A.ID
) ;
Error report -
SQL Error: ORA-01427: single-row subquery returns more than one row
01427. 00000 - "single-row subquery returns more than one row"
Any ideas?
You need a correlated subquery rather than a group by. The entire outer query doesn't look right, so I think you need something like:
UPDATE TEST_A
SET PROPERTIES = (
WITH PROPS AS (
SELECT '"'||trim(key)||'":"'||trim(value)||'"' as json,
test_b.*
FROM test_b
)
SELECT '{' || listagg(json, ',') within GROUP (order by props.key) || '}'
FROM PROPS
WHERE PROPS.A_ID = test_a.id
) ;