How to to get maximum sequence number in SQL - sql

This is the data I have in my table. What I want is maximum sequence number for each order number.
Order No seq Sta
--------------------
32100 1 rd
32100 3 rd
23600 1 rd
23600 6 rd
I want to get the following result without using cursor.
Output:
Order No seq Sta
-----------------
32100 3 rd
23600 6 rd

If you want entire records you could use ROW_NUMBER:
SELECT *
FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY Order ORDER BY No_Seq DESC) AS rn
FROM tab) s
WHERE rn = 1;
DBFiddle Demo
Please do not use keywords like Order and spaces in column names.

The most simple solution is using group by with max.
Give this a try:
select [Order No], max(seq), Sta
from myTable
group by [Order No]

Just use group by order no and order by sequence desc and you will get your record.

If you are using Oracle Database then you can use ROW_NUMBER() analytical function to achieve this result
Try the below query:
select
*
from
(select
ROW_NUMBER() OVER (PARTITION BY order_no ORDER BY seq DESC) as "ROW_NUM",
order_no, seq, sta
from
Order_Details) temp
where
temp.row_num = 1 ;
Demo

The following is probably the most efficient solution in most databases (with the right index):
select t.*
from t
where t.seq = (select max(t2.seq) from t t2 where t2.orderno = t.orderno);
You can also do this with group by:
select orderno, max(seq), sta
from t
group by orderno, sta;
Note that all columns referenced in the select are either group by keys or arguments to aggregation functions. This is proper SQL.

Related

PARTITION BY to consider only two specific columns for aggregation?

My table has the following data:
REF_NO
PRD_GRP
ACC_NO
ABC
12
1234
ABC
9C
1234
DEF
AB
7890
DEF
TY
9891
I'm trying to build a query that summarises the number of accounts per customer - the product group is irrelevant for this purpose so my expected result is:
REF_NO
PRD_GRP
ACC_NO
NO_OF_ACC
ABC
12
1234
1
ABC
9C
1234
1
DEF
AB
7890
2
DEF
TY
9891
2
I tried doing this using a window function:
SELECT
T.REF_NO,
T.PRD_GRP,
T.ACC_NO,
COUNT(T.ACC_NO) OVER (PARTITION BY T.REF_NO) AS NUM_OF_ACC
FROM TABLE T
However, the NUM_OF_ACC value returned is 2 and not 1 in the above example for the first customer (ABC). It seems that the query is simply counting the number of unique rows for each customer, rather than identifying the number of accounts as desired.
How can I fix this error?
Link to Fiddle - https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=83344cbe95fb46d4a1640caf0bb6d0b2
You need COUNT(DISTINCT, which is unfortunately not supported by SQL Server as a window function.
But you can simulate it with DENSE_RANK and MAX
SELECT
T.REF_NO,
T.PRD_GRP,
T.ACC_NO,
MAX(T.rn) OVER (PARTITION BY T.REF_NO) AS NUM_OF_ACC
FROM (
SELECT *,
DENSE_RANK() OVER (PARTITION BY T.REF_NO ORDER BY T.ACC_NO) AS rn
FROM [TABLE] T
) T;
DENSE_RANK will count up rows ordered by ACC_NO, but ignoring ties, therefore the MAX of that will be the number of distinct values.
db<>fiddle.uk
What you need is COUNT(DISTINCT T.ACC_NO) which is unfortunately not supported in window functions. Therefore you have to write a sub-query to allow you to use COUNT(DISTINCT T.ACC_NO) without a window function.
SELECT
T.REF_NO,
T.PRD_GRP,
T.ACC_NO,
-- Use of DISTINCT is not allowed with the OVER clause.
-- COUNT(DISTINCT T.ACC_NO) OVER (PARTITION BY T.REF_NO) AS NUM_OF_ACC,
(
SELECT COUNT(DISTINCT T1.ACC_NO)
FROM TEST_DATA T1
WHERE T1.REF_NO = T.REF_NO
) AS NUM_OF_ACC
FROM TEST_DATA T
The simplest way to implement count(distinct) as a window functions is by summing two dense_ranks():
SELECT T.REF_NO, T.PRD_GRP, T.ACC_NO,
(-1 +
DENSE_RANK() OVER (PARTITION BY t.REF_NO ORDER BY T.ACC_NO ASC) +
DENSE_RANK() OVER (PARTITION BY t.REF_NO ORDER BY T.ACC_NO DESC)
) as cnt_distinct
FROM TABLE T

Selecting the latest order

I need to select the data of all my customers with the records displayed in the image. But I need to get the most recent record only, for example I need to get the order # E987 for John and E888 for Adam. As you can see from the example, when I do the select statement, I get all the order records.
You don't mention the specific database, so I'll answer with a generic solution.
You can do:
select *
from (
select t.*,
row_number() over(partition by name order by order_date desc) as rn
from t
) x
where rn = 1
You can use analytical function row_number.
Select * from
(Select t.*,
Row_number() over (partition by customer_id order by order_date desc) as rn
From your_table t) t
Where rn = 1
Or you can use not exists as follows:
Select *
From yoir_table t
Where not exists
(Select 1 from your_table tt
Where t.customer_id = tt.custome_id
And tt.order_date > t.order_date)
You can do it with a subquery that finds the last order date.
SELECT t.*
FROM yoir_table t
JOIN (SELECT tt.custome_id,
MAX(tt.order_date) MaxOrderDate
FROM yoir_table tt
GROUP BY tt.custome_id) AS tt
ON t.custome_id = tt.custome_id
AND t.order_date = tt.MaxOrderDate

How to choose max of one column per other column

I am using SQL Server and I have a table "a"
month segment_id price
-----------------------------
1 1 100
1 2 200
2 3 50
2 4 80
3 5 10
I want to make a query which presents the original columns where the price will be the max per month
The result should be:
month segment_id price
----------------------------
1 2 200
2 4 80
3 5 10
I tried to write SQL code:
Select
month, segment_id, max(price) as MaxPrice
from
a
but I got an error:
Column segment_id is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause
I tried to fix it in many ways but didn't find how to fix it
Because you need a group by clause without segment_id
Select month, max(price) as MaxPrice
from a
Group By month
as you want results per each month, and segment_id is non-aggregated in your original select statement.
If you want to have segment_id with maximum price repeating per each month for each row, you need to use max() function as window analytic function without Group by clause
Select month, segment_id,
max(price) over ( partition by month order by segment_id ) as MaxPrice
from a
Edit (due to your lastly edited desired results) : you need one more window analytic function row_number() as #Gordon already mentioned:
Select month, segment_id, price From
(
Select a.*,
row_number() over ( partition by month order by price desc ) as Rn
from a
) q
Where rn = 1
I would recommend a correlated subquery:
select t.*
from t
where t.price = (select max(t2.price) from t t2 where t2.month = t.month);
The "canonical" solution is to use row_number():
select t.*
from (select t.*,
row_number() over (partition by month order by price desc) as seqnum
from t
) t
where seqnum = 1;
With the right indexes, the correlated subquery often performs better.
Only because it was not mentioned.
Yet another option is the WITH TIES clause.
To be clear, the approach by Gordon and Barbaros would be a nudge more performant, but this technique does not require or generate an extra column.
Select Top 1 with ties *
From YourTable
Order By row_number() over (partition by month order by price desc)
With not exists:
select t.*
from tablename t
where not exists (
select 1 from tablename
where month = t.month and price > t.price
)
or:
select t.*
from tablename inner join (
select month, max(price) as price
from tablename
group By month
) g on g.month = t.month and g.price = t.price

how can i write the sql query to get the result set, get the result set from the max time

Name Longititue latutute Time
tharindu 79.94148 6.9748404 00:15:47
shane 79.8630765 6.8910388 13:23:24
shane 79.862815 6.8909349 14:41:29
shane 79.8628665 6.8911084 09:39:33
shane 79.8626956 6.890992 11:00:07
shane 79.8628831 6.89099 11:43:00
i want get the result set as below
shane 79.862815 6.8909349 14:41:29
tharindu 79.94148 6.9748404 00:15:47
how can i write the sql query to get the result set, get the result set from the max time
You can try to use ROW_NUMBER window function.
SELECT Name,Longititue,latutute,[Time]
FROM (
SELECT *,ROW_NUMBER() OVER(PARTITION BY Name ORDER BY [Time] DESC) rn
FROM T
)t1
WHERE rn = 1
you can also try using correlated subquery
select * from tablename a
where Time in (select max(Time) from tablename b where a.name=b.name)
you can use corelated subquery
select t.* from table_name t
where t.[Time]=( select max([Time]) from table_name t1 where t1.Name=t.Name)
Here is a way to use ROW_NUMBER without a formal subquery:
SELECT TOP 1 WITH TIES
Name,
Longititue,
latutute,
Time
FROM yourTable
ORDER BY
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY [Time] DESC);
Demo
You can achive this use CTE(Common Table Expression) and Ranking Function.
SQL Query:
WITH CTE AS
(
SELECT Name,Longititue,latutute,Time,DENSE_RANK() OVER(PARTITION BY Name ORDER BY time desc) as RN
FROM MaxTime
)
SELECT * FROM CTE
WHERE RN = 1

sql row difference without cursor

I'm trying to get date gap(in days) between rows.
For example my data is ordered by saleDate and looks like the bellow:
ID | saleDate ID | gapInDays
10 | 1/1/2014 10 | 4 -- (5/1/2014 - 1/1/2014).Days
20 | 5/1/2014 20 | 2
30 | 7/1/2014 ====>>> 30 | 3
40 | 10/1/2014 40 | 7
50 | 17/1/2014 50 | 1 -- last row will always be 1
doing it in code is not a big deal but because the amount of row is huge (few millions) I'm trying to do so in SP level. I assume I can use cursor but i understood it is very slow.
Any solution will be highly appreciated.
Pini.
If you are using SQL SERVER 2012/Oracle/Postgres/DB2, then you have LEAD(), LAG() Functions.
select ID,saleDate,LEAD(saleDate) over (order by saleDate) DateOfNextRow
,Isnull(Datediff(dd,saleDate,LEAD(saleDate) over (order by saleDate)),1) as gapInDays
from Order
For SQL SERVER 2005/2008, you can use Window Functions like ROW_NUMBER().
If you are using MS SQL Server 2012 (or another database that supports the same, or similar functions) you can use the LAG() function to access previous rows (or LEAD() to access subsequent rows)
Apparently you want this to work on SQL Azure that lacks theLAGandLEADwindowing functions.
One solution that should work is to use theROW_NUMBERranking function applied over the date column. Azure supports theROW_NUMBERso this code should work:
select t1.id, isnull(datediff(day, t1.saledate, t2.saledate), 1) as gapInDays
from
(select id, saledate, rn = row_number() over (order by saledate, id) from gaps) t1
left join
(select id, saledate, rn = row_number() over (order by saledate, id) from gaps) t2
on t1.rn = t2.rn-1
If you want it slightly more compact (and if Azure supports ctes which I believe it does) you can do it as a common table expression:
;with c as (
select id, saledate, r = row_number() over (order by saledate, id) from gaps
)
select c.id, isnull(datediff(day, c.saledate, c2.saledate), 1) as gapInDays
from c left join c c2 on c.r = c2.rn-1
In these queries I ordered the rows by saledate, if that is incorrect you might have to change it to order by id, saledate instead if it is the id that determines order.
If your ids are strictly sequential you could do something like this
select
a.id, b.saleDate - a.saleDate
from
yourTable as a, yourTable as b
where
a.id = b.id-1
If the database is SQL Server then following query should work.
WITH Sales AS
(
SELECT
*, ROW_NUMBER() OVER (ORDER BY SaleDate) AS RowNumber
FROM
TableName
)
SELECT
DATEDIFF(DAY, T1.SaleDate, T2.SaleDate)
FROM
Sales AS T1 INNER JOIN Sales AS T2
ON T1.RowNumber = T2.RowNumber - 1;