Order by Consecutive Numbers in SQL Server Select - sql

I was wondering if there is a way to order by consecutive numbers in SQL Server 2008.
Currently I have
Select DISTINCT StoreNum, StoreName, Date, Time
From tbl_stores
ORDER BY StoreNum, Date
Which will give me
1 | Toronto Store | 2015-03-04 | 12:44:44 |
1 | Toronto Store | 2015-03-04 | 12:44:45 |
2 | Chatham Store | 2015-03-05 | 12:44:47 |
2 | Chatham Store | 2015-03-05 | 12:44:48 |
3 | London Store | 2015-03-06 | 12:44:51 |
3 | London Store | 2015-03-06 | 12:44:52 |
Is it possible to order by StoreNum consecutively then date? Like this
1 | Toronto Store | 2015-03-04 | 12:44:44 |
2 | Chatham Store | 2015-03-05 | 12:44:47 |
3 | London Store | 2015-03-06 | 12:44:51 |
1 | Toronto Store | 2015-03-04 | 12:44:45 |
2 | Chatham Store | 2015-03-05 | 12:44:48 |
3 | London Store | 2015-03-06 | 12:44:52 |
Latest Attempt:
SELECT DISTINCT StoreNum, StoreName, Date, Time,(
Select StoreNum, StoreName, Date, Time,
row_number() over (partition by StoreNum order by Date, Time) as seqnum
From tbl_stores AS q
order by seqnum, StoreNum, Date,Time
)
FROM q

Here is the idea (but without the distinct). Use row_number() to enumerate by the values within each store, then order by that:
Select StoreNum, StoreName, Date, Time,
row_number() over (partition by StoreNum order by Date, Time) as seqnum
From tbl_stores
order by seqnum, StoreNum, Date;
EDIT:
Something like:
Select StoreNum, StoreName, Date, Time,
row_number() over (partition by StoreNum order by Date, Time) as seqnum
From (select distinct StoreNum, StoreName, Date, Time
from tbl_stores s
) s
order by seqnum, StoreNum, Date;

Related

SQL query grouping by range

Hi have a table A with the following data:
+------+-------+----+--------+
| YEAR | MONTH | PA | AMOUNT |
+------+-------+----+--------+
| 2020 | 1 | N | 100 |
+------+-------+----+--------+
| 2020 | 2 | N | 100 |
+------+-------+----+--------+
| 2020 | 3 | O | 100 |
+------+-------+----+--------+
| 2020 | 4 | N | 100 |
+------+-------+----+--------+
| 2020 | 5 | N | 100 |
+------+-------+----+--------+
| 2020 | 6 | O | 100 |
+------+-------+----+--------+
I'd like to have the following result:
+---------+---------+--------+
| FROM | TO | AMOUNT |
+---------+---------+--------+
| 2020-01 | 2020-02 | 200 |
+---------+---------+--------+
| 2020-03 | 2020-03 | 100 |
+---------+---------+--------+
| 2020-04 | 2020-05 | 200 |
+---------+---------+--------+
| 2020-06 | 2020-06 | 100 |
+---------+---------+--------+
My DB is DB2/400.
I have tried with ROW_NUMBER partitioning, subqueries but I can't figure out how to solve this.
I understand this as a gaps-and-island problem, where you want to group together adjacent rows that have the same PA.
Here is an approach using the difference between row numbers to build the groups:
select min(year_month) year_month_start, max(year_month) year_month_end, sum(amount) amount
from (
select a.*, year * 100 + month year_month
row_number() over(order by year, month) rn1,
row_number() over(partition by pa order by year, month) rn2
from a
) a
group by rn1 - rn2
order by year_month_start
You can try the below -
select min(year)||'-'||min(month) as from_date,max(year)||'-'||max(month) as to_date,sum(amount) as amount from
(
select *,row_number() over(order by month)-
row_number() over(partition by pa order by month) as grprn
from t1
)A group by grprn,pa order by grprn
This works in tsql, guess you can adaot it to db2-400?
SELECT MIN(Dte) [From]
, MAX(Dte) [To]
-- ,PA
, SUM(Amount)
FROM (
SELECT year * 100 + month Dte
, Pa
, Amount
, ROW_NUMBER() OVER (PARTITION BY pa ORDER BY year * 100 + month) +
10000- (YEar*100+Month) rn
FROM tabA a
) b
GROUP BY Pa
, rn
ORDER BY [From]
, [To]
The trick is the row number function partitioned by PA and ordered by date, This'll count one up for each month for the, when added to the descnding count of month and month, you will get the same number for consecutive months with same PA. You the group by PA and the grouping yoou made, rn, to get the froups, and then Bob's your uncle.

Grouping consecutive sequences of rows

I'm trying to group consecutive rows where a boolean value is true on SQL Server. For example, here's what some source data looks like:
AccountID | ID | IsTrue | Date
-------------------------------
1 | 1 | 1 | 1/1/2013
1 | 2 | 1 | 1/2/2013
1 | 3 | 1 | 1/3/2013
1 | 4 | 0 | 1/4/2013
1 | 5 | 1 | 1/5/2013
1 | 6 | 0 | 1/6/2013
1 | 7 | 1 | 1/7/2013
1 | 8 | 1 | 1/8/2013
1 | 9 | 1 | 1/9/2013
And here's what I'd like as the output
AccountID | Start | End
-------------------------------
1 | 1/1/2013 | 1/3/2013
1 | 1/7/2013 | 1/9/2013
I have a hunch that there's some trick with grouping by partitions that will make this work but I've been unable to figure it out. I've made some progress using LAG but haven't been able to put it all together.
Thanks for the help!
This is an example of a gaps and islands problem. For this version, you just need a sequential number for each isTrue. Subtracting this number of days from each date is a constant for adjacent values that are the same:
select accountId, isTrue, min(date), max(date)
from (select t.*,
row_number() over (partition by accountId, isTrue order by date) as seqnum
from t
) t
group by accountId, isTrue, dateadd(day, -seqnum, date);
This defines all groups. If I assume that you just want values of "1" that are more than 1 day long, then:
select accountId, isTrue, min(date), max(date)
from (select t.*,
row_number() over (partition by accountId, isTrue order by date) as seqnum
from t
where isTrue = 1
) t
group by accountId, isTrue, dateadd(day, -seqnum, date)
having count(*) > 1;
You can try the following, here is the demo. I have assumption that id will always have consecutive values.
with cte as
(
select
*,
count(*) over (partition by IsTrue, rnk) as total
from
(
select
*,
id - row_number() over (partition by IsTrue order by id, date) as rnk
from myTable
) val
)
select
accountId,
min(date) as start,
max(date) as end
from cte
where total > 1
group by
accountId,
rnk
Output:
| accountid | start | end |
| --------- | ---------- | -----------|
| 1 | 2013-01-01 | 2013-01-03 |
| 1 | 2013-01-07 | 2013-01-09 |

Group similar objects in different date ranges to get min and max dates in SQL Server

I have a table:
account | onln_status | browse status | beg_date | end_date
----------+-------------+-----------------+----------+-------------
123456789 | On | Y | 1/1/2018 | 2/1/2018
123456789 | On | N | 2/2/2018 | 4/1/2018
123456789 | On | Y | 4/2/2018 | 5/1/2018
123456789 | Off | N | 5/2/2018 | 7/1/2018
123456789 | Off | Y | 7/2/2018 | 8/1/2018
123456789 | On | Y | 8/2/2018 | 10/1/2018
123456789 | On | N | 10/2/2018| 11/1/2018
and need the result to show :
account | onln_status | beg_date | end_date
----------+-------------+------------+------------
123456789 | On | 1/1/2018 | 5/1/2018
123456789 | Off | 5/2/2018 | 8/1/2018
123456789 | On | 8/2/2018 | 11/1/2018
At first, I used using min(beg date) and max(end date) but it doesn't work in this situation:
select
omsid, onln_status, min(beg_date), max(end_date)
from
table
group by
omsid, onln_status
I also tried getting a unique number whenever the previous online_status changes, but could not get a way to just add on to the number:
select
*,
case
when onln_status <> lag(onln_status) over (partition by account order by beg_date)
then 1
else 0
end as status_change
from
table
This should get you going:
CREATE TABLE Account (AccountID bigint,
onln_status varchar(3),
BrowseStatus char(1),
Beg_date date,
End_Date date);
GO
INSERT INTO Account
SELECT A, O, B, CONVERT(date,S,101), CONVERT(date,E,101)
FROM (
VALUES (123456789,'On','Y','1/1/2018','2/1/2018'),
(123456789,'On','N','2/2/2018','4/1/2018'),
(123456789,'On','Y','4/2/2018','5/1/2018'),
(123456789,'Off','N','5/2/2018','7/1/2018'),
(123456789,'Off','Y','7/2/2018','8/1/2018'),
(123456789,'On','Y','8/2/2018','10/1/2018'),
(123456789,'On','N','10/2/2018','11/1/2018')) V(A, O, B, S, E);
GO
WITH Grps AS(
SELECT *,
ROW_NUMBER() OVER (ORDER BY Beg_date) - --You may need to add a PARTITION here (I.e. on AccountID)
ROW_NUMBER() OVER (PARTITION BY onln_status ORDER BY Beg_date) AS Grp --You may need to add a PARTITION here (I.e. on AccountID)
FROM Account)
SELECT AccountID,
onln_status,
MIN(beg_date) AS beg_date,
MAX(End_date) AS End_Date
FROM Grps
GROUP BY AccountID,
onln_status,
Grp;
GO
DROP TABLE Account;
Note my comments on the use of ROW_NUMBER() though. You may need to add further partitions.

Redshift count with variable

Imagine I have a table on Redshift with this similar structure. Product_Bill_ID is the Primary Key of this table.
| Store_ID | Product_Bill_ID | Payment_Date
| 1 | 1 | 01/10/2016 11:49:33
| 1 | 2 | 01/10/2016 12:38:56
| 1 | 3 | 01/10/2016 12:55:02
| 2 | 4 | 01/10/2016 16:25:05
| 2 | 5 | 02/10/2016 08:02:28
| 3 | 6 | 03/10/2016 02:32:09
If I want to query the number of Product_Bill_ID that a store sold in the first hour after it sold its first Product_Bill_ID, how could I do this?
This example should outcome
| Store_ID | First_Payment_Date | Sold_First_Hour
| 1 | 01/10/2016 11:49:33 | 2
| 2 | 01/10/2016 16:25:05 | 1
| 3 | 03/10/2016 02:32:09 | 1
You need to get the first hour. That is easy enough using window functions:
select s.*,
min(payment_date) over (partition by store_id) as first_payment_date
from sales s
Then, you need to do the date filtering and aggregation:
select store_id, count(*)
from (select s.*,
min(payment_date) over (partition by store_id) as first_payment_date
from sales s
) s
where payment_date <= first_payment_date + interval '1 hour'
group by store_id;
SELECT
store_id,
first_payment_date,
SUM(
CASE WHEN payment_date < DATEADD(hour, 1, first_payment_date) THEN 1 END
) AS sold_first_hour
FROM
(
SELECT
*,
MIN(payment_date) OVER (PARTITION BY store_id) AS first_payment_date
FROM
yourtable
)
parsed_table
GROUP BY
store_id,
first_payment_date

Aggregate/Windowed Function To Find Min and Max of Sequential Rows

I've got a SQL table where I want to find the first and last dates of a group of records, providing they're sequential.
Patient | TestType | Result | Date
------------------------------------------
1 | 1 | A | 2012-03-04
1 | 1 | A | 2012-08-19
1 | 1 | B | 2013-05-27
1 | 1 | A | 2013-06-20
1 | 2 | X | 2012-08-19
1 | 2 | X | 2013-06-20
2 | 1 | B | 2014-09-09
2 | 1 | B | 2015-04-19
Should be returned as
Patient | TestType | Result | StartDate | EndDate
--------------------------------------------------------
1 | 1 | A | 2012-03-04 | 2012-08-19
1 | 1 | B | 2013-05-27 | 2013-05-27
1 | 1 | A | 2013-06-20 | 2013-06-20
1 | 2 | X | 2012-08-19 | 2013-06-20
2 | 1 | B | 2014-09-09 | 2015-04-19
The problem is that if I just group by Patient, TestType, and Result,
then the first and third rows in the example above would become a single row.
Patient | TestType | Result | StartDate | EndDate
--------------------------------------------------------
1 | 1 | A | 2012-03-04 | 2013-06-20
1 | 1 | B | 2013-05-27 | 2013-05-27
1 | 2 | X | 2012-08-19 | 2013-06-20
2 | 1 | B | 2014-09-09 | 2015-04-19
I feel like there's got to be something clever I can do with a partition, but I can't quite figure out what it is.
There are several ways to approach this. I like identifying the groups using the difference of row number values:
select patient, testtype, result,
min(date) as startdate, max(date) as enddate
from (select t.*,
(row_number() over (partition by patient, testtype order by date) -
row_number() over (partition by patient, testtype, result order by date)
) as grp
from table t
) t
group by patient, testtype, result, grp
order by patient, startdate;
select patient, testtype, result, date as startdate,
isnull(lead(date) over(partition by patient, testtype, result order by date), date) as enddate
from tablename;
You can use lead function to get the value of date (as enddate) from the next row in each group.
SQL Fiddle with sample data.
See if this gives you what you need.
with T1 as (
select
*,
case when lag(Patient,1)
over (order by Patient, TestType, Result) = Patient
and lag(TestType,1)
over (order by Patient, TestType, Result) = TestType
and lag(Result,1)
over (order by Patient, TestType, Result) = Result
then null else 1 end as Changes
from t
), T2 as (
select
Patient,
TestType,
Result,
dt,
sum(Changes) over (
order by Patient, TestType, Result, dt
) as seq
from T1
)
select
Patient,
TestType,
Result,
min(dt) as dtFrom,
max(dt) as dtTo
from T2
group by Patient, TestType, Result, seq
order by Patient, TestType, Result