Sql query for start and end of a column - sql

I am trying to write a query to get start and end serials for a particular sale
Serial Sale_id
0001 1
0002 1
0003 1
0004 2
0005 2
0006 1
0007 1
0008 1
I am looking for a result like
Quantity Start End
3 0001 0003
3 0006 0008
can some one please help me get this one right

This query (adapted from the book SQL MVP Deep Dives) should give you what you want:
SELECT
Sale_id, --optional, include is you want the Sale_id
-- if Serial is a (VAR)CHAR type use this field
Quantity = MAX(CAST(Serial AS INT)) - MIN(CAST(Serial AS INT)) + 1,
-- if Serial is INT already use this field instead
--Quantity = MAX(CAST(Serial AS INT)) - MIN(CAST(Serial AS INT)) + 1,
[Start] = MIN(Serial),
[End] = MAX(Serial)
FROM (
SELECT
Sale_id,
Serial,
RowNumber =
Serial - ROW_NUMBER() OVER (PARTITION BY Sale_id ORDER BY Serial)
FROM YourTable
) a
--WHERE Sale_id = 1 -- optional limiting WHERE clause
--WHERE Sale_id IN (1,2) -- optional limiting WHERE clause
GROUP BY Sale_id, RowNumber
ORDER BY Start;
I made the assumption that Serial is stored as a character type and included a cast to INT. If it is an numeric type already, change to the line without the cast for the Quantity.
I choose to include the Sale_id column in the output, if you don't want it, just comment it out. If you want to limit the results to one or more particular Sale_id include one of the the WHERE clauses.
Sample SQL Fiddle
Sample output:
Sale_id Quantity Start End
----------- ----------- ---------- ----------
1 3 0001 0003
2 2 0004 0005
1 3 0006 0008
(3 row(s) affected)

You can do this by counting the number of non-similar sales ids before each one. This is a constant for sequences where the sales ids are the same. Then use this for grouping:
select count(*), min(serial_no), max(serial_no)
from (select t.*,
(select count(*)
from taple t2
where t2.serial_no <= t.serial_no and
t2.sale_id <> t.sale_id
) as grp
from taple t
) t
group by grp;
If you only care about sale_id = 1 then put a where clause in the middle subquery query.
Here is a SQL Fiddle.

Related

Finding next node in T-SQL

Suppose I have the following table (Date + CustNum is an unique index)
RowId
Date
CustNum
1
1-Jan-2021
0001
2
1-Jan-2021
0002
3
1-Jan-2021
0004
4
2-Jan-2021
0001
5
3-Jan-2021
0001
6
3-Jan-2021
0004
7
7-Jan-2021
0004
The table has ~500K records.
What is the best method to get the previous and next rowid of the CustNum?
RowId
Date
CustNum
CustPrevRowId
CustNextRowId
1
1-Jan-2021
0001
4
2
1-Jan-2021
0002
3
1-Jan-2021
0004
6
4
2-Jan-2021
0001
1
5
5
3-Jan-2021
0001
4
6
3-Jan-2021
0004
3
7
7
7-Jan-2021
0004
6
I've tried to use sub-query but I have faced a performance issue.
SELECT T1.*,
(SELECT TOP 1 RowID FROM T T2 WHERE T2.CustNum = T1.CustNum AND T2.Date < T1.Date ORDER BY DATE DESC) AS CustPrevRowId,
(SELECT TOP 1 RowID FROM T T2 WHERE T2.CustNum = T1.CustNum AND T2.Date > T1.Date ORDER BY DATE ) AS CustNextRowId
FROM T T1
As already pointed in the comments, you can use the two window functions:
LAG, retrieves the previous row in the same partition, given a specified order
LEAD, does the same, but will get the following row instead
In this specific case, you want to:
partition on "CustNum" (since you want last row for each customer number)
order by the date field (so that it will attempt to get rowid with respect to last/next date)
SELECT *, LAG([RowId]) OVER(PARTITION BY [CustNum] ORDER BY [Date]) AS CustPrevRowId,
LEAD([RowId]) OVER(PARTITION BY [CustNum] ORDER BY [Date]) AS CustNextRowId
FROM tab
ORDER BY RowId
Check the demo here.
Note: the last ORDER BY RowId clause is not necessary.

How to calculate the turnover 1 month ago with the day and month values ​kept as int in SQL Server

This is my table:
id
Total
Date
1
3
410
2
4
121
3
7
630
4
8
629
5
9
101
Date part is saved as int month and day. How to find the total amount made in the previous month of the current month?
Try the following query:
Select Date_/100 as Date_Month, Sum(Total) as MonthlyTotal From YourTable
Where MONTH(GETDATE()) - 1 = (Date_/100)
Group By (Date_/100)
See a demo from db<>fiddle.
I concur with the comments made above. I get it that you can't change the way that you store your "dates" . But you should make whoever made that decision miserable. They are asking for trouble like that.
Having said that -
I rely, here, on the fact that the division of two integers, in SQL Server, if it is not an integer already, is truncated to the next lower integer, and returned as integer.
WITH
-- your input, don't use in final query ...
-- I renamed the third column more aptly to "monthday"
indata(id,Total,monthday) AS (
SELECT 1,3.00,410
UNION ALL SELECT 2,4.55,121
UNION ALL SELECT 3,7.40,630
UNION ALL SELECT 4,8.00,629
)
-- real query starts here - replace following comma with "WITH"
,
per_month AS (
SELECT
monthday / 100 AS monthno
, SUM(total) AS monthtot
FROM indata
GROUP BY monthno
-- ctl monthno | monthtot
-- ctl ---------+----------
-- ctl 1 | 4.55
-- ctl 4 | 3.00
-- ctl 6 | 15.40
)
SELECT
*
FROM per_month
WHERE monthno=MONTH(CURRENT_DATE) - 1;
-- out monthno | monthtot
-- out ---------+----------
-- out 6 | 15.40

How to get the MAX value of unique column in sql and aggregate other?

I want get the row with max 'date', groupy just by unique 'id' but without considering another columns.
I tried this query:
(But don't work cause modify anothers columns)
SELECT id,
MAX(num),
MAX(date),-- I just want the max of this column
MAX(product_name),
MAX(other_columns)
FROM TB
GROUP BY id
Table:
id num date product_name other_columns
123 0001 2021-12-01 exit 12315413
123 0002 2021-12-02 entry 65481328
333 0001 2021-12-03 entry 13848136
333 ASDV 2021-12-04 exit 1325165
Expected Result:
id num date product_name
123 0002 2021-12-02 entry
333 ASDV 2021-12-04 exit
How to do that?
Sub-query with an inner join can take care of this pretty DBMS agnostically.
SELECT
t.ID
,t.date
,t.product_name
,t.other_columns
FROM tb as t
INNER JOIN (
SELECT
id
,MAX(date) as date
FROM tb
GROUP BY id
) as s on t.id = s.id and t.date = s.date

SQL retrieval from a table

Sales table
ItemID Date Code
-------------------------------------------------
12345 2019-02-17 1
12345 2019-02-17 2
12345 2019-02-17 3
-------------------------------------------------
12344 2019-02-18 1
12344 2019-02-18 3
12344 2019-02-18 2
-------------------------------------------------
12443 2019-02-19 1
12443 2019-02-19 2
12443 2019-02-19 3
I want to retrieve those item ids from Sales table where Code 2 is updated after code 3. So my query should return 12344 as result... I cannot think of any functions to achieve this as I have limited knowledge on SQL. Can someone please provide me some suggestion on achieving this? Thanks
Here is the query that you are looking for
select sales1.itemId from sales sales1
inner join sales sales2
on sales1.itemId = sales2.itemId
where sales1.date > sales2.date and sales1.code = 2 and sales2.code = 3;
Check if it helps you.
You can try using lag()
select itemid from
(
select sales1.itemId,sales1.code,
sales1.code-lag(code) over(partition by itemid order by dates) as diff
from sales sales1
where code in (2,3)
)A where diff>=1
You can include a column
update_seq_no
Each time you update you can increase the update_seq_no and then you can achieve your requirement you want using the below sql.
alter table sales add (update_seq_no number);
create SEQUENCE IFSQ_UPDATE_SEQNO MINVALUE 1000 MAXVALUE 9999999999999999 INCREMENT BY 1 START WITH 1 NOCYCLE ;
-- for existing rows (just a work around)
BEGIN
for rec in (select rowid row_id,itemid,date from sales where update_Seq_no is null order by 2,3 asc)
loop
update sales set update_Seq_no=IFSQ_UPDATE_SEQNO.NEXTVAL
where rowid=rec.row_id;
commit;
end loop;
end;
--for new rows
insert into sales values(p_itemid,p_date,3,IFSQ_UPDATE_SEQNO.NEXTVAL);
insert into sales values(p_itemid,p_date,2,IFSQ_UPDATE_SEQNO.NEXTVAL);
with sales1 as (select row_num1,a.* from (select rownum row_num1,b.*
from sales b order by update_seq_no asc)a)
select a.*
from sales1 a,sales1 b
where a.row_num1-1=b.row_num1
and a.code=2 and b.code=3
and a.itemid=b.itemid
and a.Date=b.Date;
If there is only one row per itemid and code, then you can simply aggregate:
select itemid
from mytable
group by itemid
having max(case when code = 2 then date end) > max(case when code = 3 then date end);
With your sample data, however, no itemid will be returned, as all codes per itemid have the same date. None comes before the other.

Most Efficient SQL to Calculate Running Streak Occurrences

I am looking for the most efficient manner to determine the longest occurrence of a streak within a given data set; specifically, to determine the longest winning streak of games.
Below is the SQL that I have thus far, and it does seem to perform very fast, and as expected from the limited testing I've done on a dataset with around 100,000 records.
DECLARE #HistoryDateTimeLimit datetime = '3/15/2018';
CTE to create result subset from voting dataset.
WITH Results AS (
SELECT
EntityPlayerId,
(CASE
WHEN VoteTeamA = 1 AND ParticipantAScore > ParticipantBScore THEN 'W'
WHEN VoteTeamA = 0 AND ParticipantBScore > ParticipantAScore THEN 'W'
ELSE 'L'
END) AS WinLoss,
match.ScheduledStartDateTime
FROM
[dbo].[MatchVote] vote
INNER JOIN [dbo].[MatchMetaData] match ON vote.MatchId = match.MatchId
WHERE
IsComplete = 1
AND ScheduledStartDateTime >= #HistoryDateTimeLimit
)
CTE to create subset of data with streak type as WinLoss and total count of votes in the partition using ROW_NUMBER().
WITH Streaks AS (
SELECT
EntityPlayerId,
ScheduledStartDateTime,
WinLoss,
ROW_NUMBER() OVER (PARTITION BY EntityPlayerId ORDER BY ScheduledStartDateTime) -
ROW_NUMBER() OVER (PARTITION BY EntityPlayerId, WinLoss ORDER BY ScheduledStartDateTime) AS Streak
FROM
Results
)
CTE to summarize the partitioned vote streaks by WinLoss and a begin date/time, with the total count in the streak.
WITH StreakCounts AS (
SELECT
EntityPlayerId,
WinLoss,
MIN(ScheduledStartDateTime) StreakStart,
MAX(ScheduledStartDAteTime) StreakEnd,
COUNT(*) as Streak
FROM
Streaks
GROUP BY
EntityPlayerId, WinLoss, Streak
)
CTE to select the MAXIMUM (longest) vote streak for WinLoss of W (win) grouped by players.
WITH LongestWinStreak AS (
SELECT
EntityPlayerId,
MAX(Streak) AS LongestStreak
FROM
StreakCounts
WHERE
WinLoss = 'W'
GROUP BY
EntityPlayerId
)
Selecting the useful data from the LongestWinStreak CTE.
SELECT * FROM LongestWinStreak
This is the 3rd iteration of the code; at first I feel like I was overthinking and using windows with the LAG function to define a reset period that was later used for partitioning.
[UPDATE]: SQLFiddle example at http://sqlfiddle.com/#!18/5b33a/1 -- Sample data for the two tables that are used above are as follows.
The data is meant to show the schema, and can be extrapolated for your own testing/usage;
MatchVote table data.
EntityPlayerId IsExtMatch MatchId VoteTeamA VoteDateTime IsComplete
-------------------- ------------ -------------------- --------- ----------------------- ----------
158 1 152639 0 2018-03-20 23:25:28.910 1
158 1 156058 1 2018-03-13 23:36:57.517 1
MatchMetaData table data.
MatchId IsTeamTournament MatchCompletedDateTime ScheduledStartDateTime MatchIsFinalized TournamentId TournamentTitle TournamentLogoUrl TournamentLogoThumbnailUrl GameName GameShortCode GameLogoUrl ParticipantAScore ParticipantAName ParticipantALogoUrl ParticipantBScore ParticipantBName ParticipantBLogoUrl
--------- ---------------- ----------------------- ----------------------- ---------------- -------------------- ------------------ ----------------------- ---------------------------- --------------------------------- -------------- ----------------------- ------------------ ------------------- --------------------- ----------------- ------------------- --------------------
23354 1 2014-07-30 00:30:00.000 2014-07-30 00:00:00.000 1 543 Sample https://...Small.png https://...Small.png Dota 2 Dota 2 https://...logo.png 3 Natus Vincere.US https://...VI.png 0 Not Today https://...ay.png
44324 1 2014-12-15 12:40:00.000 2014-12-15 11:40:00.000 1 786 Sample https://...Small.png https://...Small.png Counter-Strike: Global Offensive CS:GO https://...logo.png 0 Avalier's stars https://...oto.png 1 Kassad's Legends https://...oto.png