Related
i'm facing issues migrating queries from Oracle to Postgres 15. How can i join a table that is already joined? The Oracle looks like this:
select ltd1.short_description || '_' || to_char( CASE WHEN lsd.origine_type_id = 2 THEN
lsd.data_normalizzazione_mail
ELSE lsd.data_normalizzazione_web
END, 'YYYYMMDD_HH24MISS' ) Transazione,
lkc.kit_name || '_' || lkc.doc_name Documento_Idkit_iddoc,
lp.codice_pratica Pratica,
to_char( CASE WHEN lsd.origine_type_id = 2 THEN lsd.data_normalizzazione_mail ELSE lsd.data_normalizzazione_web END, 'DD/MM/YYYY HH24:MI:SS' ) Data_Inizio,
ltd1.long_description Tipologia_Documento,
CASE WHEN lsd.origine_type_id = 2 THEN 'Digitale' ELSE 'Cartaceo' END Tipologia_Arrivo,
lsc.sla_period || ' ore' Sla,
to_char( lsd.data_fine_trans, 'DD/MM/YYYY HH24:MI:SS' ) Data_Fine_Lavorazione,
ltd2.short_description Fase_Lavorazione,
case when ((lsd.sla_period * 60) - lsd.min_elapsed) <= 0 then 999999999 else ((lsd.sla_period * 60) - lsd.min_elapsed) end Tempo_Residuo,
to_char( lsd.data_prevista_fine_sla, 'DD/MM/YYYY HH24:MI:SS' ) Data_Previsione_Chiusura_SLA,
lsd.alert_sla Alert,
lsd.stato_sla_type_id Stato_Lav,
lsd.in_sla,
lsd.out_of_sla OutSla
from gdf_suez.ld_sla_detail lsd,
gdf_suez.ls_type_description ltd1,
gdf_suez.ld_documenti ld,
gdf_suez.ls_kit_config lkc,
gdf_suez.ld_pratiche lp,
gdf_suez.ls_sla_config lsc,
gdf_suez.ls_type_description ltd2,
gdf_suez.ld_mail lm
where ltd1.context = 'DOC_TYPE_ID'
and ltd1.type_id = lsd.doc_type_id
and ld.trans_id(+) = lsd.trans_id
and lm.trans_id(+) = lsd.trans_id
and lkc.kit_id(+) = ld.kit_id
and lp.pratica_id(+) = ld.pratica_id
and lsc.doc_type_id = lsd.doc_type_id
and lsc.doc_subtype_id = lsd.doc_subtype_id
and lsc.giorno_id = 1
and ltd2.context = 'DOC_STATUS_TYPE_ID'
and ltd2.type_id = lsd.stato_sla_type_id
and decode( lsd.origine_type_id, 2, lsd.data_normalizzazione_mail, lsd.data_normalizzazione_web) between to_date( '2022-02-01', 'YYYY-MM-DD' ) and to_timestamp( '2022-02-28 23:59:59', 'YYYY-MM-DD HH24:MI:SS' )
and lsd.stato_trans_type_id >= 0
and lm.email_stato_type_id not in (6,16)
order by Alert desc, Stato_Lav, Tempo_Residuo
When i try to rewrite it on postgres, the table gdf_suez.ld_documenti ld needs to be joined to 3 tables, and one of those 3 needs to be joined to another table. How do i fix this?
Tried to rewrite the query but the error "table name "ld" specified more than once" is thrown
from gdf_suez.ld_sla_detail lsd
LEFT OUTER JOIN gdf_suez.ld_documenti ld ON lsd.trans_id = ld.trans_id
LEFT OUTER JOIN gdf_suez.ld_mail lm ON lsd.trans_id = lm.trans_id,
gdf_suez.ls_type_description ltd1,
gdf_suez.ld_documenti ld
LEFT OUTER JOIN gdf_suez.ls_kit_config lkc ON ld.kit_id = lkc.kit_id
LEFT OUTER JOIN gdf_suez.ld_pratiche lp ON ld.pratica_id = lp.pratica_id,
gdf_suez.ls_sla_config lsc,
gdf_suez.ls_type_description ltd2
It is always easy to translate from Oracle's outdated outer join syntax to the more powerful syntax of the SQL standard: put everything that has a (+) on the right side of a LEFT JOIN. So your FROM clause would become
FROM gdf_suez.ls_type_description AS ltd1
JOIN gdf_suez.ld_sla_detail AS lsd
ON ltd1.type_id = lsd.doc_type_id
JOIN gdf_suez.ls_sla_config AS lsc
ON lsc.doc_type_id = lsd.doc_type_id
AND lsc.doc_subtype_id = lsd.doc_subtype_id
JOIN gdf_suez.ls_type_description AS ltd2
ON ltd2.type_id = lsd.stato_sla_type_id
LEFT JOIN gdf_suez.ld_mail AS lm
ON lm.trans_id = lsd.trans_id
LEFT JOIN gdf_suez.ld_documenti AS ld
ON ld.trans_id = lsd.trans_id
LEFT JOIN gdf_suez.ls_kit_config AS lkc
ON lkc.kit_id = ld.kit_id
LEFT JOIN gdf_suez.ld_pratiche AS lp
ON lp.pratica_id = ld.pratica_id
I have the payment table:
There could be erroneous entries when a payment was made by mistake - see row 5 and then, this payment gets cancelled out - see row 6. I cannot figure out the query where I don't only cancel the negative amounts but also the corresponding pair. Here is the desired outcome:
You could also see the cases when several wrong payments were made and then, I need to cancel out all payments which if summed up give the cancelled amount.
The desired outcome:
I found Remove Rows That Sum Zero For A Given Key, Selecting positive aggregate value and ignoring negative in Postgres SQL and https://www.sqlservercentral.com/forums/topic/select-all-negative-values-that-have-a-positive-value but it is not exactly what I need
I already don't mind cases like case 2. At least, find a reliable way to exclude those like 5;-5.
you can try this for deleting the rows from the table :
WITH RECURSIVE cancel_list (id, total_cancel, sum_cancel, index_to_cancel) AS
( SELECT p.id, abs(p.amount), 0, array[p.index]
FROM payment_table AS p
WHERE p.amount < 0
AND p.id = id_to_check_and_cancel -- this condition can be suppressed in order to go through the full table payment
UNION ALL
SELECT DISTINCT ON (l.id) l.id, l.total_cancel, l.sum_cancel + p.amount, l.index_to_cancel || p.index
FROM cancel_list AS l
INNER JOIN payment_table AS p
ON p.id = l.id
WHERE l.sum_cancel + p.amount <= l.total_cancel
AND NOT l.index_to_cancel #> array[p.index] -- this condition is to avoid loops
)
DELETE FROM payment_table AS p
USING (SELECT DISTINCT ON (c.id) c.id, unnest(c.index_to_cancel) AS index_to_cancel
FROM cancel_list AS c
ORDER BY c.id, array_length(c.index_to_cancel, 1) DESC
) AS c
WHERE p.index = c.index_to_cancel;
you can try this for just querying the table without the hidden rows :
WITH RECURSIVE cancel_list (id, total_cancel, sum_cancel, index_to_cancel) AS
( SELECT p.id, abs(p.amount), 0, array[p.index]
FROM payment_table AS p
WHERE p.amount < 0
AND p.id = id_to_check_and_cancel -- this condition can be suppressed in order to go through the full table payment
UNION ALL
SELECT DISTINCT ON (l.id) l.id, l.total_cancel, l.sum_cancel + p.amount, l.index_to_cancel || p.index
FROM cancel_list AS l
INNER JOIN payment_table AS p
ON p.id = l.id
WHERE l.sum_cancel + p.amount <= l.total_cancel
AND NOT l.index_to_cancel #> array[p.index] -- this condition is to avoid loops
)
SELECT *
FROM payment_table AS p
LEFT JOIN (SELECT DISTINCT ON (c.id) c.id, c.index_to_cancel
FROM cancel_list AS c
ORDER BY c.id, array_length(c.index_to_cancel, 1) DESC
) AS c
ON c.index_to_cancel #> array[p.index]
WHERE c.index_to_cancel IS NULL ;
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;
I'm going to post my query at the end of this post here, but just exposition is required first. Please ignore the column names and table names but I have syntax errors in two spots. When I put this query with my CTE it tells me I have to first 'terminate the previous statement with a ;' Then I go onto alias a column name in the CTE and then it says 'The multi-part identifier "E.ActiveAGVs" could not be bound.'
I hope that I am explaining my problem well enough. If anyone can see what I'm trying to do and let me know if it will work or correct my syntax errors, I would really appreciate it.
Select A.move_hour as 'Hour',
isnull(B.move_count,0) as 'Current_Count',
isnull(C.move_count,0) as '1_Day_Previous',
isnull(D.move_count,0) as '2_Day_Previous',
ISNULL (E.ActiveAGVs,0) as 'Active AGV''s'
--^ Error right here
from
(select distinct(DATEPART(HH, Move_History.Move_Dt)) as move_hour
from Move_History
where Plant_Id = 1 and Building_Id = 1) as A
left outer join
(select datepart(HH,Move_History.Move_Dt) as move_hour,
Move_History.Move_Cnt as move_count
from Move_History
Group by datepart(HH,Move_History.Move_Dt), Move_Cnt) as B on A.move_hour = B.move_hour
left outer join
(select datepart(HH,Move_History.Move_Dt) as move_hour, Move_History.Move_Cnt as move_count
from Move_History
Group by datepart(HH,Move_History.Move_Dt), Move_Cnt) as C on A.move_hour = C.move_hour
left outer join
(select datepart(HH,Move_History.Move_Dt) as move_hour, Move_History.Move_Cnt as move_count
from Move_History
Group by datepart(HH,Move_History.Move_Dt), Move_Cnt) as D on A.move_hour = D.move_hour;
with const as (
select cast(cast(getdate() as date) as datetime) as midnight
),
allhours as (
select 0 as m_hour, midnight as timestart, dateadd(hour, 1, midnight) as timeend from const union all
...
select 23 as m_hour, dateadd(hour, 23, midnight) as timestart, dateadd(hour, 24, midnight) as timeend from const
)
(select ah.m_hour,
(sum(datediff(SECOND, timestart), ah.timeend else dt.End_Dt end))
/ 18000.0) * 5 as ActiveAGVs
from allhours as ah
left outer join AGV_Report as dt
on ah.timestart< coalesce(dt.End_dt, getdate()) and
ah.timeend >= dt.Begin_Dt
Group by datepart(SECOND,ah.hour), ah.timestart) as E on A.move_hour = E.move_hour
--^ 'Incorrect syntax near "as"'
where A.move_hour is not null
order by ah.m_hour asc
All CTEs that you need to define for a statement, must go at the beginning of the statement. Even though the CTEs are going to be used only in one of the subqueries, the syntax still requires them to be placed at the beginning of the entire statement, not at the beginning of the particular subquery where they are actually referenced.
Therefore, your statement should probably look something like this:
; -- required if there are statements preceding
with const as (
select cast(cast(getdate() as date) as datetime) as midnight
),
allhours as (
select
0 as m_hour,
midnight as timestart,
dateadd(hour, 1, midnight) as timeend
from const
union all
...
select
23 as m_hour,
dateadd(hour, 23, midnight) as timestart,
dateadd(hour, 24, midnight) as timeend
from const
)
Select A.move_hour as 'Hour',
isnull(B.move_count,0) as 'Current_Count',
isnull(C.move_count,0) as '1_Day_Previous',
isnull(D.move_count,0) as '2_Day_Previous',
ISNULL (E.ActiveAGVs,0) as 'Active AGV''s'
from
(
select distinct
DATEPART(HH, Move_History.Move_Dt) as move_hour
from Move_History
where Plant_Id = 1 and Building_Id = 1
) as A
left outer join
(
select
datepart(HH,Move_History.Move_Dt) as move_hour,
Move_History.Move_Cnt as move_count
from Move_History
Group by datepart(HH,Move_History.Move_Dt), Move_Cnt
) as B on A.move_hour = B.move_hour
left outer join
(
select
datepart(HH,Move_History.Move_Dt) as move_hour,
Move_History.Move_Cnt as move_count
from Move_History
Group by datepart(HH,Move_History.Move_Dt), Move_Cnt
) as C on A.move_hour = C.move_hour
left outer join
(
select
datepart(HH,Move_History.Move_Dt) as move_hour,
Move_History.Move_Cnt as move_count
from Move_History
Group by datepart(HH,Move_History.Move_Dt), Move_Cnt
) as D on A.move_hour = D.move_hour
left outer join -- assuming...
(
select
ah.m_hour,
(sum(datediff(SECOND, timestart), ah.timeend else dt.End_Dt end))
/ 18000.0) * 5 as ActiveAGVs
from allhours as ah
left outer join AGV_Report as dt
on ah.timestart < coalesce(dt.End_dt, getdate())
and ah.timeend >= dt.Begin_Dt
Group by datepart(SECOND,ah.hour), ah.timestart
) as E on A.move_hour = E.move_hour
where A.move_hour is not null
order by ah.m_hour asc
A common table expression (CTE) must be separated from any prior statement by a semicolon (;). If no statements precede the with then there is no need for a semicolon.
You can have multiple CTEs within a single with. An example follows:
with
-- Counting numbers.
Numbers as (
select 1 as Number
union all
select Numbers.Number + 1
from Numbers
where Number < 10 ),
-- Squares of counting numbers.
Squares as (
select Number, Number * Number as Square
from Numbers )
-- Result.
select Number, Square
from Squares
Like the rest of the people trying to help, I have no idea of what your SQL is trying to do.
How can I change following query, so that I'm able to parameterize the SparePartNames?
It returns all ID's of repairs where not all mandatory spareparts were changed, in other words where at least one part is missing.
Note that the number of spareparts might change in future not only the names. Is it possible without using a stored procedure with dynamic SQL? If not, how could this SP look like?
Edit: Note that i do not need to know how to pass a list/array as parameter, this is asked myriads of time on SO. I've also already a Split table-valued-function. I'm just wondering how i could rewrite the query to be able to join(or whatever) with a list of mandatory parts, so that i'll find all records where at least one part is missing. So is it possible to use a varchar-parameter like '1264-3212,1254-2975' instead of a list of NOT EXISTS? Sorry for the confusion if it was not clear in the first place.
SELECT d.idData
FROM tabData d
INNER JOIN modModel AS m ON d.fiModel = m.idModel
WHERE (m.ModelName = 'MT27I')
AND (d.fiMaxServiceLevel >= 2)
AND (d.Manufacture_Date < '20120511')
AND (NOT EXISTS
(SELECT NULL
FROM tabDataDetail AS td
INNER JOIN tabSparePart AS sp ON sp.idSparePart = td.fiSparePart
WHERE (td.fiData = d.idData)
AND (sp.SparePartName = '1264-3212'))
OR (NOT EXISTS
(SELECT NULL
FROM tabDataDetail AS td
INNER JOIN tabSparePart AS sp ON sp.idSparePart = td.fiSparePart
WHERE (td.fiData = d.idData)
AND (sp.SparePartName = '1254-2975'))
)
)
Unfortunately I don't see how I could use sp.SparePartName IN/NOT IN(#sparePartNames) here.
One way to do it is to create a function to split delimited strings:
CREATE FUNCTION [dbo].[Split]
(
#Delimiter char(1),
#StringToSplit varchar(512)
)
RETURNS table
AS
RETURN
(
WITH Pieces(pieceNumber, startIndex, delimiterIndex)
AS
(
SELECT 1, 1, CHARINDEX(#Delimiter, #StringToSplit)
UNION ALL
SELECT pieceNumber + 1, delimiterIndex + 1, CHARINDEX(#Delimiter, #StringToSplit, delimiterIndex + 1)
FROM Pieces
WHERE delimiterIndex > 0
)
SELECT
SUBSTRING(#StringToSplit, startIndex, CASE WHEN delimiterIndex > 0 THEN delimiterIndex - startIndex ELSE 512 END) AS Value
FROM Pieces
)
populate a table variable with the spare part names:
DECLARE #SpareParts TABLE
(
SparePartName varchar(50) PRIMARY KEY CLUSTERED
);
INSERT INTO #SpareParts
SELECT Value FROM dbo.Split(',', '1264-3212,1254-2975');
and then join to the table variable:
SELECT d.idData
FROM tabData d
INNER JOIN modModel AS m ON d.fiModel = m.idModel
WHERE (m.ModelName = 'MT27I')
AND (d.fiMaxServiceLevel >= 2)
AND (d.Manufacture_Date < '20120511')
AND EXISTS (
SELECT 1
FROM tabDataDetail AS td
INNER JOIN tabSparePart AS sp ON sp.idSparePart = td.fiSparePart
LEFT JOIN #SpareParts AS s ON s.SparePartName = sp.SparePartName
WHERE td.fiData = d.idData
AND s.SparePartName IS NULL
)
Assuming there is (or will be) a table or view of mandatory spare parts, a list of exists can be replaced with a left join to tabDataDetail / tabSparePart pair on SparePartName; non-matches are reported back using td.fiSparePart is null.
; with mandatorySpareParts (SparePartName) as (
select '1264-3212'
union all
select '1254-2975'
)
SELECT d.idData
FROM tabData d
INNER JOIN modModel AS m ON d.fiModel = m.idModel
WHERE (m.ModelName = 'MT27I')
AND (d.fiMaxServiceLevel >= 2)
AND (d.Manufacture_Date < '20120511')
AND exists
(
SELECT null
from mandatorySpareParts msp
left join ( tabDataDetail AS td
INNER JOIN tabSparePart AS sp
ON sp.idSparePart = td.fiSparePart
AND td.fiData = d.idData
)
ON msp.SparePartName = sp.SparePartName
WHERE td.fiSparePart is null
)
Part names should be replaced by their id's, which would simplify left join and speed the query up.
EDIT: i've errorneously left filtering of td in where clause, which invalidated left join. It is now in ON clause where it belongs.
Use a table-variable and join on that.