Syntax Error: Lateral Join with Delete (PostgreSQL) - postgresql

PostgreSQL 11.1
AFAIK, this is correct and should run. It Fails with syntax error on Delete. What am I missing?
Thanks for any help.
ERROR: syntax error at or near "DELETE"
LINE 41: DELETE FROM d
WITH _in (tservice, patient_recid, disease_recid, new_disease_recid) AS (
VALUES ('2021-04-21'::timestamp, '23262'::integer, '34978'::integer, '33364'::integer)
)
UPDATE dx d
SET disease_recid = n.new_disease_recid
FROM _in n,
LATERAL ( WITH RECURSIVE readtoend AS(
SELECT recid, newrecid
FROM patients p1
JOIN _in n ON p1.recid = n.patient_recid
UNION
SELECT c.recid, c.newrecid
FROM patients c
INNER JOIN readtoend s ON s.newrecid = c.recid
),
readtostart AS(
SELECT recid, newrecid
FROM patients p1
JOIN _in n ON p1.recid = n.patient_recid
UNION
SELECT c.recid, c.newrecid
FROM patients c
INNER JOIN readtostart s ON s.recid = c.newrecid
)
SELECT recid FROM readtoend
UNION
SELECT recid FROM readtostart
) j,
LATERAL ( WITH _get_existing_target AS(
SELECT d.*
FROM d
WHERE (d.patient_recid, d.disease_recid) = (j.recid, n.new_disease_recid) AND d.tservice <= n.tservice
),
_get_conflicts AS(
SELECT d.*
FROM d
WHERE (d.patient_recid, d.disease_recid) = (j.recid, n.disease_recid) AND EXISTS ( SELECT 1
FROM _get_existing_target x
WHERE d.patient_recid = x.patient_recid AND d.tservice::date = x.tservice::date)
)
DELETE FROM d
USING _get_conflicts f
WHERE d.recid = f.recid
RETURNING d.*
) del
WHERE (d.patient_recid, d.disease_recid) = (j.recid, n.disease_recid) AND d.tservice::date <= n.tservice::date
AND d.recid NOT IN ( SELECT recid FROM del);

You cannot use DELETE ... RETURNING in the FROM list of a query.

Related

How can I update a table using the following CROSS APPLY?

UPDATE ItemDim_DEV
set ActiveFlag = 0,
EndUTCDate = GETUTCDATE()
from (select i.SKU, i.itemname, i.category, i.CategoryInternalId, i.itemtype, i.IsActive, i.assetaccount, i.InternalId,
ni.sku,
ni.itemname,
ni.class_name,
ni.productclass,
ni.ItemType,
ni.isactive,
ni.assetaccount,
i.CalculatedHash, ni.hashid
from (
SELECT i.*
FROM itemDim_Dev i
WHERE i.SourceSystem = 'NetSuite'
--and InternalId = '1692'
) i
CROSS APPLY (
SELECT (CONVERT([binary](64),hashbytes('SHA2_512',
concat (ISNULL(im.TargetSKU, ni.sku), ni.itemname, pc.class_name, ni.productclass, ni.ItemType,
CAST(CASE WHEN ni.IsInactive = 'T' THEN 0 ELSE 1 END AS BIT ), ni.assetaccount)))) hashid,
ISNULL(im.TargetSKU, ni.sku) sku,
ni.itemname,
pc.class_name,
ni.productclass,
ni.ItemType,
CAST(CASE WHEN ni.IsInactive = 'T' THEN 0 ELSE 1 END AS BIT ) isactive, ni.assetaccount
FROM NetSuiteInventory ni
INNER JOIN Product_Class pc ON ni.productclass = pc.class_id
LEFT JOIN ItemMapping im ON ni.sku = im.SourceSKU
WHERE ni.ItemInternalId = CAST(i.InternalId as bigint)
and
(CONVERT([binary](64),hashbytes('SHA2_512',
concat (ISNULL(im.TargetSKU, ni.sku), ni.itemname, pc.class_name, ni.productclass, ni.ItemType,
CAST(CASE WHEN ni.IsInactive = 'T' THEN 0 ELSE 1 END AS BIT ), ni.assetaccount)))) = i.CalculatedHash
) ni
I understand that I can put a select after the from of the update but in the CROSS APPLY that is in the query starts with a select as in the second one, can you help me please.
Make a join to the target table
UPDATE A
SET XXX = YYY
FROM ItemDim_DEV A
JOIN (
--huge SELECT that I recommend you try to shorten in the next questions
CROSS APPLY (any secrets...)
) B ON A.PK = B.PK

PostgreSQL Join with special condition

Lets assume we have the following table1:
1 2 3
a x m
a y m
b z m
I want to do an inner join on the table
INNER JOIN tabel2 ON table1.2 = table2.2
Somehow like this, but additional a condition that the value of table1.1 not unique. Thus on table1.1 = b no inner join will occure in this example.
What is the best way to achieve this?
Using a an aggregate in a sub query is how I would do it
SELECT *
FROM table1
JOIN table2
ON table1."2" = table2."2"
JOIN (
SELECT "1"
FROM table1
GROUP BY "1"
HAVING COUNT(*) > 1
) AS sub_q
ON sub_q."1" = table1."1";
Another option might be a cte or temporary table to hold the rows you're joining on
WITH _cte AS
(
SELECT "1"
FROM table1
GROUP BY "1"
HAVING COUNT(*) > 1
)
SELECT *
FROM table1
JOIN table2
ON table1."2" = table2."2"
JOIN _cte AS cte
ON cte."1" = table1."1";
temp table:
CREATE TEMPORARY TABLE _tab
(
"1" varchar
);
INSERT INTO _tab
SELECT "1"
FROM table1
GROUP BY "1"
HAVING COUNT(*) > 1;
SELECT *
FROM table1
JOIN table2
ON table1."2" = table2."2"
JOIN _tab AS tab
ON tab."1" = table1."1";

Compare varchar string to produce missing items list

I have a table with a column. The column stores locations using varchar as the datatype. The locations use the format -2,7 -25,30 etc. I am trying to produce a list of missing locations i.e. where we don't have any customers.
The locations go from -30,-30 to 30,30. I can't find a way to setup a loop to run though all the options. Is there a way to do this?
Microsoft SQL Server 2017
;WITH cte as (
select -30 as n --anchor member
UNION ALL
select n + 1 --recursive member
from cte
where n < 31
)
select z.*
from (
select CONCAT(y.n,',',x.n) as locations
from cte as x CROSS JOIN cte y
) as z
LEFT OUTER JOIN dbo.Client as cli ON cli.client_location = z.locations
where cli.client_location IS NULL
order by z.locations asc
Generate all combinations.
Then match the generated against the existing combinations.
WITH DIGITS AS
(
SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS val(n)
),
NUMS AS
(
SELECT (tens.n * 10 + ones.n)-50 AS n
FROM DIGITS ones
CROSS JOIN DIGITS tens
),
LOCATIONS AS
(
SELECT CONCAT(n1.n,',',n2.n) AS location, n1.n as n1, n2.n as n2
FROM NUMS n1
JOIN NUMS n2 ON n2.n BETWEEN -30 AND 30
WHERE n1.n BETWEEN -30 AND 30
)
SELECT loc.location
FROM LOCATIONS loc
LEFT JOIN
(
SELECT Client_Location, COUNT(*) Cnt
FROM dbo.Client
GROUP BY Client_Location
) cl ON cl.Client_Location = loc.location
WHERE cl.Client_Location IS NULL
ORDER BY loc.n1, loc.n2
I would go with a recursive CTE. This is a slight variation of SNR's approach:
with cte as (
select -30 as n --anchor member
union all
select n + 1 --recursive member
from cte
where n < 30
)
select cte.x, cte.y,
concat(cte_x.n, ',', cte_y.n) as missing_location
from cte cte_x cross join
cte cte_y left join
dbo.client c
on c.client_location = concat(cte_x.n, ',', cte_y.n)
where c.client_location is null;
Or to avoid the concat() twice:
select cte.x, cte.y, v.location as missing_location
from cte cte_x cross join
cte cte_y cross apply
(values (concat(cte_x.n, ',', cte_y.n))
) v(location) left join
dbo.client c
on c.client_location = v.location
where c.client_location is null;

Avoiding Order By in T-SQL

Below sample query is a part of my main query. I found SORT operator in below query is consuming 30% of the cost.
To avoid SORT, there is need of creation of Indexes. Is there any other way to optimize this code.
SELECT TOP 1 CONVERT( DATE, T_Date) AS T_Date
FROM TableA
WHERE ID = r.ID
AND Status = 3
AND TableA_ID >ISNULL((
SELECT TOP 1 TableA_ID
FROM TableA
WHERE ID = r.ID
AND Status <> 3
ORDER BY T_Date DESC
), 0)
ORDER BY T_Date ASC
Looks like you can use not exists rather than the sorts. I think you'll probably get a better performance boost by use a CTE or derived table instead of the a scalar subquery.
select *
from r ... left outer join
(
select ID, min(t_date) as min_date from TableA t1
where status = 3 and not exists (
select 1 from TableA t2
where t2.ID = t1.ID
and t2.status <> 3 and t2.t_date > t1.t_date
)
group by ID
) as md on md.ID = r.ID ...
or
select *
from r ... left outer join
(
select t1.ID, min(t1.t_date) as min_date
from TableA t1 left outer join TableA t2
on t2.ID = t1.ID and t2.status <> 3
where t1.status = 3 and t1.t_date < t2.t_date
group by t1.ID
having count(t2.ID) = 0
) as md on md.ID = r.ID ...
It also appears that you're relying on an identity column but it's not clear what those values mean. I'm basically ignoring it and using the date column instead.
Try this:
SELECT TOP 1 CONVERT( DATE, T_Date) AS T_Date
FROM TableA a1
LEFT JOIN (
SELECT ID, MAX(TableA_ID) AS MaxAID
FROM TableA
WHERE Status <> 3
GROUP BY ID
) a2 ON a2.ID = a1.ID AND a1.TableA_ID > coalesce(a2.MAXAID,0)
WHERE a1.ID = r.ID AND a1.Status = 3
ORDER BY T_Date ASC
The use of TOP 1 in combination with the unexplained r alias concern me. There's almost certainly a MUCH better way to get this data into your results that doesn't involve doing this in a sub query (unless this is for an APPLY operation).

Postgresql query COUNT and MAX together?

SELECT id,icon,type,cnt
FROM capability
JOIN (
SELECT s0_.capability_id AS capability_id0 ,
count(capability_id) as cnt
FROM service_offer_capability s0_
INNER JOIN service_offer s1_ ON s0_.service_offer_id = s1_.id
WHERE s0_.value <> 'i:0;' AND s1_.service_id = 2
GROUP BY s0_.capability_id
) af
ON af.capability_id0=id;
All i want to do is to have a max(cnt) as an extra column. I know that you can order by cnt and get the first but i am looking for an alternative..Is it possible or i have to run multiple queries?
This should do it:
SELECT id,
icon,
type,
cnt,
max(cnt) over () as max_cnt
FROM capability
JOIN (
SELECT s0_.capability_id AS capability_id0 ,
count(capability_id) as cnt
FROM service_offer_capability s0_
INNER JOIN service_offer s1_ ON s0_.service_offer_id = s1_.id
WHERE s0_.value <> 'i:0;' AND s1_.service_id = 2
GROUP BY s0_.capability_id
) af
ON af.capability_id0=id;