SQL Sequence increasing/resetting based on crtieria - sql

I'm trying to write a query in SQL but have hit a brick wall.
My results table looks similar to the below:
Category Date
1234 15/07/2014
1234 17/07/2014
1234 29/07/2014
1234 31/07/2014
1234 02/08/2014
1234 04/08/2014
1234 06/08/2014
1211 17/07/2014
1211 06/08/2014
1211 08/08/2014
I'm trying to create a sequence which resets when the difference in dates is greater than 2 aswell as unique to the category as follows:
Category Date Sequence
1234 15/07/2014 1
1234 17/07/2014 2
1234 29/07/2014 1
1234 31/07/2014 2
1234 02/08/2014 3
1234 04/08/2014 4
1234 06/08/2014 5
1211 17/07/2014 1
1211 06/08/2014 1
1211 08/08/2014 2

This can be accomplished using the CONDITIONAL_TRUE_EVENT function. Thanks to sKwa & scutter for the help!
SELECT Category,
Date,
RANK()
OVER (
PARTITION BY Category, seq
ORDER BY Date)
FROM (SELECT Category,
Date,
CONDITIONAL_TRUE_EVENT(DATEDIFF(day, prev_Date, Date) > 2)
OVER (
PARTITION BY Category
ORDER BY Date) AS seq
FROM (SELECT *,
LAG(Date, 1)
OVER (
PARTITION BY Category
ORDER BY Date) prev_Date
FROM test) q) q2
ORDER BY Category,
Date;

Related

SQL - Group vacations in a table based on a holidays

Here is the sample data from the employee vacation table.
Emp_id Vacation_Start_Date Vacation_End_Date Public_Hday
1234 06/01/2022 06/07/2022 null
1234 06/08/2022 06/14/2022 null
1234 06/15/2022 06/19/2022 06/17/2022
1234 06/20/2022 06/23/2022 null
1234 06/24/2022 06/28/2022 null
1234 06/29/2022 07/02/2022 06/30/2022
1234 07/03/2022 07/07/2022 null
1234 07/08/2022 07/12/2022 null
1234 07/13/2022 07/17/2022 07/15/2022
1234 07/18/2022 07/22/2022 null
I want to group these vacations based on the public holidays in between (Assuming that all the vacations are consecutive). Here is the output that I am trying to get.
Emp_id Vacation_Start_Date Vacation_End_Date Public_Hday Group
1234 06/01/2022 06/07/2022 null 0
1234 06/08/2022 06/14/2022 null 0
1234 06/15/2022 06/19/2022 06/17/2022 1
1234 06/20/2022 06/23/2022 null 1
1234 06/24/2022 06/28/2022 null 1
1234 06/29/2022 07/02/2022 06/30/2022 2
1234 07/03/2022 07/07/2022 null 2
1234 07/08/2022 07/12/2022 null 2
1234 07/13/2022 07/17/2022 07/15/2022 3
1234 07/18/2022 07/22/2022 null 3
Here is the code that I tried
Select *, dense_rank() over (partition by Emp_id order by Public_Hday) - 1 AS Group from Emp_Vacation.
But, it gave the expected group values only to the vacations where the Public_Hday is not null. How do I get the group values to the other vacations.
You can use a conditional sum() over()
Select *
,Grp = sum( case when [Public_Hday] is null then 0 else 1 end ) over (partition by [Emp_id] order by [Vacation_Start_Date])
from YourTable
Results

Joins and/or Sub queries or Ranking functions

I have a table as follows:
Order_ID
Ship_num
Item_code
Qty_to_pick
Qty_picked
Pick_date
1111
1
1
3000
0
Null
1111
1
2
2995
1965
2021-05-12
1111
2
1
3000
3000
2021-06-24
1111
2
2
1030
0
Null
1111
3
2
1030
1030
2021-08-23
2222
1
3
270
62
2021-03-18
2222
1
4
432
0
Null
2222
2
3
208
0
Null
2222
2
4
432
200
2021-05-21
2222
3
3
208
208
2021-08-23
2222
3
4
232
200
2021-08-25
From this table,
I only want to show the rows that has the latest ship_num information, not the latest pick_date information (I was directed to a question like this that needed to return the rows with the latest entry time, I am not looking for that) for an order i.e., I want it as follows
Order_ID
Ship_num
Item_code
Qty_to_pick
Qty_picked
Pick_date
1111
3
2
1030
1030
2021-08-23
2222
3
3
208
208
2021-08-23
2222
3
4
232
200
2021-08-25
I tried the following query,
select order_id, max(ship_num), item_code, qty_to_pick, qty_picked, pick_date
from table1
group by order_id, item_code, qty_to_pick, qty_picked, pick_date
Any help would be appreciated.
Thanks in advance.
Using max(ship_num) is a good idea, but you should use the analytic version (with an OVER clause).
select *
from
(
select t.*, max(ship_num) over (partition by order_id) as orders_max_ship_num
from table1 t1
) with_max
where ship_num = orders_max_ship_num
order by order_id, item_code;
You can get this using the DENSE_RANK().
Query
;with cte as (
select rnk = dense_rank()
over (Partition by order_id order by ship_num desc)
, *
from table_name
)
Select *
from cte
Where rnk =1;

Continuous Date / Not continuous Date sql server

I'm encountering a problem with continous date / not cointinuous date on sql server 2012.
I have a table that looks like this :
Article
Creation date
1234
04/01/2021
1234
05/01/2021
1234
06/01/2021
1234
07/01/2021
1234
10/01/2021
1234
12/01/2021
12345
02/01/2021
12345
03/01/2021
12345
17/01/2021
123456
01/01/2021
123456
03/01/2021
123456
05/01/2021
The problem is :
I want to get the count of every article by continuous date with the min date of the range, it's a bit difficult to explain what I want but there is an example of the result :
Article
Creation date
Count
1234
04/01/2021
4
1234
10/01/2021
1
1234
12/01/2021
1
12345
02/01/2021
2
12345
17/01/2021
1
123456
01/01/2021
1
123456
03/01/2021
1
123456
05/01/2021
1
For example :
count of 1st row = 4 because there is 4 continous day on the range 04/01/2021 to 07/01/2021
count of 2nd row = 1 because there is only 1 day, 0 continuous day with 10/01/2021 for this article
count of 3rd row = 1 because there is only 1 day, 0 continuous day with 12/01/2021 for this article
I'm starting with that :
;WITH CTE AS (
SELECT Article, [Creation date], StartDate= Dateadd(day,-ROW_NUMBER() OVER (ORDER BY [Creation date]),[Creation date])
FROM MyTable
)
SELECT Article, min([Creation date]) as [Creation date], count(Article) as count
FROM CTE
GROUP BY StartDate, Article, [Creation date]
order by Article, [Creation date]
Output :
Article
Creation date
Count
1234
04/01/2021
1
1234
05/01/2021
1
1234
06/01/2021
1
1234
07/01/2021
1
1234
10/01/2021
1
1234
12/01/2021
1
12345
02/01/2021
1
12345
03/01/2021
1
12345
17/01/2021
1
123456
01/01/2021
1
123456
03/01/2021
1
123456
05/01/2021
1
but the result is wrong, I don't really know how to approach this problem. If someone can enlighten me, appreciate.
Thank you
This is an example of a gaps-and-islands problem. The simplest solution in this case is to subtract an increasing sequence of values and aggregate. This works because the difference is constant for incremental dates:
select article, min(creation_date), max(creation_date), count(*)
from (select t.*,
row_number() over (partition by article order by creation_date) as seqnum
from mytable t
) t
group by article, dateadd(day, -seqnum, creation_date)
order by article, min(creation_date);

SQL difference in counter between two dates

I have a table like the below:
ID, MachineID Customer TimeStamp Counter type
1 A ABC 2017-10-25 3:08PM 1952 1
2 A ABC 2017-10-25 3:00PM 1940 1
3 A ABC 2017-10-25 12:05PM 1920 1
4 A ABC 2017-10-25 9:00AM 1900 1
5 B BCD 2017-10-25 3:11PM 1452 1
6 B BCD 2017-10-25 3:10PM 1440 1
7 B BCD 2017-10-25 12:15PM 1420 1
8 B BCD 2017-10-25 9:30AM 1400 1
9 A ABC 2017-10-23 3:08PM 1900 1
10 A ABC 2017-10-23 3:00PM 1840 1
11 A ABC 2017-10-23 12:05PM 1820 1
12 A ABC 2017-10-23 9:00AM 1800 1
13 B BCD 2017-10-23 3:11PM 1399 1
14 B BCD 2017-10-23 3:10PM 1340 1
15 B BCD 2017-10-23 12:15PM 1320 1
16 B BCD 2017-10-23 9:30AM 1300 1
The counter value increases whenever there is a click. I am trying to calculate number of clicks for each day by taking maximum counter value at the end of day and subtract the previous day maximum counter value and so on.
How do I do this in SQL server. Have to repeat this for each customer and Machine
Try this. I am using LAG function in order to achieve this. You can use where clause to filter out specific date you want :
Create table #counter(ID int, timeStamp datetime, Counter int, type int)
insert into #counter values
(1, '20171024 3:08PM' ,1952, 1),
(1, '20171025 3:00PM' ,1964, 1)
Select iq.*, (iq."counter" - iq.yesterday_counter) as today_count
from
(select id,
cast("timestamp" as date) as today_date,
"counter",
LAG("counter") over (order by cast("timestamp" as date)) yesterday_counter
from #counter
) iq
output:
id today_date counter yesterday_counter today_count
----------- ---------- ----------- ----------------- -----------
1 2017-10-24 1952 NULL NULL
1 2017-10-25 1964 1952 12
A SQL query to get the max counter for each day is:
SELECT CAST(timeStamp as date) AS [dateval]
,MAX(Counter) AS [maxCounter]
FROM YOURDATASET
GROUP BY CAST(timeStamp as date)
This is converting the datetime to date- cutting out the time, then taking the max(Counter).
One method to get the difference is to save the result in a temp datastructure, then query it to get the difference.
The question is whether your previous date is exactly the previous day, or if you're skipping days between counts, or taking the weekend off, etc. In that case you have to select the greatest previous date to the date being examined.
ex.
DECLARE #temp TABLE (dateval date, maxCounter int)
INSERT INTO #temp(dateval, maxCounter)
SELECT CAST(timeStamp as date) AS [dateval]
,MAX(Counter)
FROM YOURDATASET
GROUP BY CAST(timeStamp as date)
SELECT T.dateval
,T.dateval
-
(SELECT maxCounter
FROM #temp T2
WHERE T2.dateVal = (SELECT MAX(dateVal)
FROM #temp T3
WHERE T3.dateVal < T1.dateVal
)
) AS [Difference]
FROM #temp T
ORDER BY T.dateval

I require to create an SQL or PLSQL Query for merging and ordering data in a table

I am trying to create a SQL query that will help me get a proper ordered output from the below data.
Data in table :
Cust num Eff_Date Exp_date
1001 1234 10-01-2010 20-06-2010
1001 1234 20-06-2010 25-06-2010
1001 1234 25-06-2010 12-02-2011
1001 1234 12-02-2011 12-02-2011
1001 3456 12-02-2011 25-07-2012
1001 3456 25-07-2012 25-07-2012
1001 1234 25-07-2012 25-07-2012
1001 1234 25-07-2012 31-12-4700
Expected output of Query :
Cust num Eff_Date Exp_date
1001 1234 10-01-2010 12-02-2011
1001 3456 12-02-2011 25-07-2012
1001 1234 25-07-2012 31-12-4700
I would prefer to be able to do the above using a single SQL statement. Is it possible to do the above using a single SQL statement? Is there an alternate way to do the above.
SELECT
Customer,
`number` AS Number,
MIN(Eff_Date) AS Eff_Date,
MAX(Exp_date) AS Exp_date
FROM tablename
GROUP BY Customer, number
in Oracle, we can use analytic functions to group islands together:
SQL> select c.cust, c.num, min(eff_date) eff_date, max(exp_Date) exp_date
2 from (select c.cust, c.num, c.eff_date, c.exp_date, max(rn) over (partition by cust, num order by eff_date) grp
3 from (select c.cust, c.num, c.eff_date, c.exp_date,
4 case
5 when lag(exp_date, 1) over (partition by cust, num order by eff_date) != eff_date
6 then
7 row_number() over (partition by cust, num order by eff_date)
8 when row_number() over (partition by cust, num order by eff_date) = 1
9 then
10 1
11 end rn
12 from cust c) c) c
13 group by c.cust, c.num, grp
14 order by eff_date;
CUST NUM EFF_DATE EXP_DATE
---------- ---------- ---------- ----------
1001 1234 10-01-2010 12-02-2011
1001 3456 12-02-2011 25-07-2012
1001 1234 25-07-2012 31-12-4700
SQL>
This works for postgres, but it could be adapted to oracle with minor changes, IMHO.
Note: I changed the data a bit, because overlapping intervals don't look plausible to me.
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;
CREATE TABLE lutser
( cust INTEGER NOT NULL
, num iNTEGER NOT NULL
, eff_date DATE NOT NULL
, exp_date DATE NOT NULL
, PRIMARY KEY (cust, num, eff_date)
);
SET datestyle=german;
INSERT INTO lutser(cust,num,eff_date,exp_date) VALUES
(1001,1234,'10-01-2010', '20-06-2010' )
,(1001,1234,'20-06-2010', '25-06-2010' )
,(1001,1234,'25-06-2010', '12-02-2011' )
,(1001,1234,'12-02-2011', '12-02-2011' )
,(1001,3456,'12-02-2011', '25-07-2012' )
,(1001,3456,'25-07-2012', '25-07-2012' )
,(1001,1234,'25-07-2012', '25-08-2012' ) -- added a month to get unique PK
,(1001,1234,'25-08-2012', '31-12-4700' ) -- and here as well
;
VACUUM ANALYZE lutser;
-- SELECT * FROM lutser ORDER BY cust,num,eff_date;
-- EXPLAIN ANALYZE
WITH RECURSIVE island AS
( SELECT cust,num,eff_date,exp_date
FROM lutser l0
WHERE NOT EXISTS
( SELECT *
FROM lutser nx
WHERE nx.cust = l0.cust AND nx.num = l0.num
AND nx.eff_date < l0.eff_date
AND nx.exp_date >= l0.eff_date
)
UNION -- ALL
SELECT isl.cust,isl.num, isl.eff_date,l1.exp_date
FROM lutser l1
JOIN island isl ON isl.cust = l1.cust AND isl.num = l1.num
AND isl.eff_date < l1.eff_date
AND isl.exp_date >= l1.eff_date
)
SELECT DISTINCT ON (cust,num,eff_date) *
FROM island
ORDER BY cust,num,eff_date
;
Result:
NOTICE: drop cascades to table tmp.lutser
DROP SCHEMA
CREATE SCHEMA
SET
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "lutser_pkey" for table "lutser"
CREATE TABLE
SET
INSERT 0 8
VACUUM
cust | num | eff_date | exp_date
------+------+------------+------------
1001 | 1234 | 10.01.2010 | 20.06.2010
1001 | 1234 | 25.07.2012 | 25.08.2012
1001 | 3456 | 12.02.2011 | 25.07.2012
(3 rows)