Unable to use LAG function - sql

I need to update a table as :
ID | START_DATE | response| FINAL_TREND
1 14-10-2021 4
1 15-10-2021 3
1 16-10-2021 2
1 17-10-2021 2
1 18-10-2021 3
1 19-10-2021 2
OUTPUT AS:
ID | START_DATE | response| FINAL_TREND
1 14-10-2021 4 NULL
1 15-10-2021 3 4
1 16-10-2021 2 3
1 17-10-2021 2 2
1 18-10-2021 3 2
1 19-10-2021 2 3
So, when running a code:
SELECT LAG(RESPONSE,1) OVER (ORDER BY START_DATE) AS NEW
FROM DUMMY_YC
Output as:
NULL
4
3
2
3
2
2
But when using same code in update AS:
UPDATE DUMMY_YC A SET A.RESPONSE = (SELECT LAG(B.RESPONSE,1) OVER (ORDER BY B.START_DATE) AS NEW
FROM DUMMY_YC B WHERE B.START_DATE=A.START_DATE)
output as:
7 rows updated.
But the actual updated value is
RESPONSE|
(null)
(null)
(null)
(null)
(null)
(null)
(null)
Helps will be appreciated. Working On Oracle SQL Developer.

I'd go with merge.
Before:
SQL> SELECT *
2 FROM test
3 ORDER BY id, start_date;
ID START_DATE RESPONSE FINAL_TREND
---------- ---------- ---------- -----------
1 14.10.2021 4 0
1 15.10.2021 3 0
1 16.10.2021 2 0
1 17.10.2021 2 0
1 18.10.2021 3 0
1 19.10.2021 2 0
6 rows selected.
Merge:
SQL> MERGE INTO test a
2 USING (SELECT b.id,
3 b.start_date,
4 b.response,
5 LAG (b.response, 1) OVER (ORDER BY b.start_date) AS final_trend
6 FROM test b) x
7 ON ( x.id = a.id
8 AND x.start_date = a.start_date)
9 WHEN MATCHED
10 THEN
11 UPDATE SET a.final_trend = x.final_trend;
6 rows merged.
After:
SQL> SELECT *
2 FROM test
3 ORDER BY id, start_date;
ID START_DATE RESPONSE FINAL_TREND
---------- ---------- ---------- -----------
1 14.10.2021 4
1 15.10.2021 3 4
1 16.10.2021 2 3
1 17.10.2021 2 2
1 18.10.2021 3 2
1 19.10.2021 2 3
6 rows selected.
SQL>

Related

postgresql how to check by two columns from join

There are some points and point_types with mark actual:
points
------
id point_type
------
1 1
2 1
3 2
point_types
------
id actual
------
1 true
2 false
Also there are directions:
directions
------
id point_from point_to
------
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 3
7 3 1
8 3 2
9 3 3
I need only directions with actual point_from and point_to:
id point_from point_to
------
1 1 1
2 1 2
4 2 1
5 2 2
Trying with:
SELECT d.id, d.point_from, d.point_to
FROM directions d
JOIN points p ON (d.point_from = p.id OR d.point_to = p.id)
JOIN point_types pt ON pt.id = p.TYPE AND pt.actual = TRUE
GROUP BY 1,2,3
but getting directions with actual point_from or actual point_to :
id point_from point_to
------
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 3
7 3 1
8 3 2
Use a CTE that returns all the ids of the not actual points and then with NOT EXISTS get only the directions that do not contain any of them:
WITH cte AS (
SELECT p.id
FROM points p INNER JOIN point_types pt
ON pt.id = p.point_type
WHERE pt.actual = false
)
SELECT d.*
FROM directions d
WHERE NOT EXISTS (SELECT * FROM cte c WHERE c.id IN (d.point_from, d.point_to));
See the demo.

Generate a serial number based on quantity column in sql

Hi Experts I have a table like this
T1
Order_no
Qty
1
3
2
5
3
1
4
3
I need to generate a column 'serial no' having values based on 'qty'
Output needed
OrderNo
Qty
SerailNo
1
3
1
1
3
2
1
3
3
2
5
1
2
5
2
2
5
3
2
5
4
2
5
5
3
1
1
4
3
1
4
3
2
4
3
3
Any suggestions?
Thanks in advance!!
You don't mention the specific database so I'll assume you are using PostgreSQL, aren't you?
You can use a Recursive CTE to expand the rows. For example:
with recursive
n as (
select order_no, qty, 1 as serial_no from t1
union all
select order_no, qty, serial_no + 1
from n
where serial_no < qty
)
select * from n order by order_no, serial_no
Result:
order_no qty serial_no
--------- ---- ---------
1 3 1
1 3 2
1 3 3
2 5 1
2 5 2
2 5 3
2 5 4
2 5 5
3 1 1
4 3 1
4 3 2
4 3 3
See running example at DB Fiddle.
EDIT FOR ORACLE
If you are using Oracle the query changes a bit to:
with
n (order_no, qty, serial_no) as (
select order_no, qty, 1 from t1
union all
select order_no, qty, serial_no + 1
from n
where serial_no < qty
)
select * from n order by order_no, serial_no
Result:
ORDER_NO QTY SERIAL_NO
--------- ---- ---------
1 3 1
1 3 2
1 3 3
2 5 1
2 5 2
2 5 3
2 5 4
2 5 5
3 1 1
4 3 1
4 3 2
4 3 3
See running example at db<>fiddle.
You should first provide the database you're using. Whether it's oracle, Sql Server, PostGreSQL will determine which procedural language to use. It's very likely that you'll need to do this in two steps:
1st: Duplicate the number of rows based on the column Qty using a decreasing loop
2nd: You'll need to create a sequential partionned column based on the Qty column

case when... for subgroups

I have table like this:
receipt
position
unit
booking time
1
1
1
08:00:00
1
2
1
08:00:05
1
3
1
08:00:11
1
4
1
08:00:18
1
5
1
08:00:21
1
6
5
08:00:25
1
1
1
08:00:30
1
2
1
08:00:33
1
3
1
08:00:37
1
4
1
08:00:40
1
5
1
08:00:49
2
1
1
08:01:55
2
2
1
08:01:58
2
3
1
08:02:04
3
1
1
08:02:20
3
2
5
08:02:24
3
1
1
08:02:30
3
2
1
08:02:35
I want to check for every receipt whether unit 5 exists or not. If unit 5 exists, I only want to select positions with a booking time after the entry with unit 5.
For the example above my result therefore should look like this:
receipt
position
unit
bookingtime
1
1
1
08:00:30
1
2
1
08:00:33
1
3
1
08:00:37
1
4
1
08:00:40
1
5
1
08:00:49
2
1
1
08:01:55
2
2
1
08:01:58
2
3
1
08:02:04
3
1
1
08:02:30
3
2
1
08:02:35
I have kind of a start, which delivers the right result if there was only one receipt:
Select * from test
where bookingtime> (case
when (select Max(bookingtime) from test where unit=5) is null
then (Select convert(time,'00:00:00'))
Else (select Max(bookingtime) from testdb where unit=5)
End)
What am I missing to let this code run through every single receipt separately so that I get the result I am looking for?
You can use a window function to get the time for unit 5:
select t.*
from (select t.*,
min(case when unit = 5 then bookingtime end) over (partition by receipt) as bookingtime_5
from t
) t
where bookingtime_5 is null or
bookingtime > bookingtime_5;

Get TOP 1 row from multiple tables based on date

I have five tables in a SQL Server database. These tables are listed below and I want to select Data from these tables according to date. I tried searching but could not find solution for multiple tables. Please help
TABLE1
id PId CId
----------- ---------- ------------
1 P001 1
2 P002 2
3 P003 4
4 P004 5
TABLE2
id CId CNo ConId
----------- ---------- ------------ ----------
1 1 1 C123
2 1 2 PA444
3 1 3 PA456
4 2 1 AUX2398
5 2 2 AUX2345
6 4 1 PA123
7 5 1 C234
TABLE3
id CId CNo Label Date
----------- ---------- ------------ ---------- ----------
1 1 1 A 1/1/2000
2 1 2 A 15/10/2020
3 1 3 A 20/10/2020
4 2 1 A 15/10/2020
5 2 2 A 20/10/2020
6 4 1 A 20/10/2020
7 5 1 A 27/10/2020
TABLE4
id CId CNo Label Date
----------- ---------- ------------ ---------- ----------
1 1 1 B 20/10/2020
2 1 2 B 27/10/2020
3 1 3 B 22/10/2020
4 2 1 B 22/10/2020
5 2 2 B 26/10/2020
6 4 1 B 22/10/2020
7 5 1 B 30/10/2020
TABLE5
id CId CNo Label Date
----------- ---------- ------------ ---------- ----------
1 1 1 C 26/10/2020
2 1 2 C 1/1/2000
3 1 3 C 23/10/2020
4 2 1 C 25/10/2020
5 2 2 C 30/10/2020
6 4 1 C 25/10/2020
7 5 1 C 1/1/2000
I want to select Label and Date from Table 3, 4 and 5 where Date is >1/1/2000 and < than and close to 24/10/2020 and grouped according to PId, CId, ConId and CNo.
Desired result:
PId CId ConId CNo Label Date
-------- ---------- ---------- -------- --------- ----------
P001 1 C123 1 B 20/10/2020
P001 1 PA444 2 A 15/10/2020
P001 1 PA456 3 C 23/10/2020
P002 2 AUX2398 1 B 22/10/2020
P002 2 AUX2345 2 A 20/10/2020
P003 4 PA123 1 B 22/10/2020
P004 5 C234 1 - -
Any help will be appreciated. Thank you.
You can achieve this with a couple of CTE's; the first forms a UNION of TABLE3, TABLE4 and TABLE5; the second generates a row number based on the Date descending for each partition of PId, CId, ConId and CNo. We then select all rows from the second CTE where the row number is 1:
WITH CTE AS (
SELECT * FROM Table3 WHERE date > '2000-01-01'
UNION ALL
SELECT * FROM Table4 WHERE date > '2000-01-01'
UNION ALL
SELECT * FROM Table5 WHERE date > '2000-01-01'
),
CTE2 AS (
SELECT t1.PId, t1.CId, t2.ConId, t2.CNo, CTE.Label, CTE.Date,
ROW_NUMBER() OVER (PARTITION BY t1.PId, t1.CId, t2.ConId, t2.CNo ORDER BY CTE.Date DESC) AS rn
FROM TABLE1 t1
JOIN TABLE2 t2 ON t2.CId = t1.CId
LEFT JOIN CTE ON CTE.Cid = t2.CId AND CTE.CNo = t2.CNo AND CTE.Date < '2020-10-24'
)
SELECT PId, CId, ConId, CNo, Label, Date
FROM CTE2
WHERE rn = 1
ORDER BY PId, CId, CNo
Output:
PId CId ConId CNo Label Date
P001 1 C123 1 B 2020-10-20
P001 1 PA444 2 A 2020-10-15
P001 1 PA456 3 C 2020-10-23
P002 2 AUX2398 1 B 2020-10-22
P002 2 AUX2345 2 A 2020-10-20
P003 4 PA123 1 B 2020-10-22
P004 5 C234 1 - -
Demo on dbfiddle

How to Subtotal Value with MAX

1.I have data as follows (just a subset - there are 20K records)
sku,id
1 1
1 2
1 2
1 2
1 3
1 4
1 1
1 2
1 3
1 4
1 4
1 4
1 5
1 6
1 6
2 1
2 1
2 2
2 3
2 3
2 3
2 4
2 4
2 5
2 5
2 6
2 7
2 1
2 2
2 3
The above values translate to
1 = 4 records
1 = 6 records
2 = 7 records
2 = 3 records
The MAX would just give me 6 for one and 7 for 2
The actual total is 1 = 10 and 2 = 10
How do I sum up to get the correct values?
You can use order by and some way of limiting rows. In standard SQL this would be:
select t.*
from t
order by id desc
fetch first 2 rows only;
However, some databases might use limit or select top or some other method.
No handling of ties here. Thousands of other questions handle this topic.
select sku, id
from (
select *, row_number() over (order by id desc) rn
from T
) t
where rn <= 2
order by rn desc;