How to give group numbers based on a condition in sql - sql

Here is my table. I am using Snowflake
CREATE TABLE testx
(
c_order int,
c_condition varchar(10)
);
INSERT INTO testx
VALUES (1, 'and'), (2, 'and'), (3, 'or'), (4, 'and'), (5, 'or');
SELECT * FROM testx
c_order c_condition
--------------------
1 and
2 and
3 or
4 and
5 or
I am trying to write a query which will give me group numbers based on the fact that consecutive 'and's should be with same group number. when 'or' comes, it should increase the group number. by the way, we should maintain the c_order also.
Here is the expected result:
c_order c_condition group_no
-------------------------------
1 and 1
2 and 1
3 or 2
4 and 2
5 or 3
I have tried using dense_rank like this:
SELECT
*,
DENSE_RANK() OVER (ORDER BY c_condition)
FROM testx
But it doesn't return exactly what I want. Can somebody please help?`

Idea is to use same value for C_ORDER as group_no if
C_ORDER is more then previous OR's c_order.
In CTE we only select rows with OR and assign them a
group number using ROW_NUMBER() generator -
Main query -
with temp_cte as
(
select c_order,
case -- to check if 'or' is the first row or not
when (select min(c_order) from testx where c_condition='or') =
(select min(c_order) from testx)
then row_number() over (order by c_order)
else
row_number() over (order by c_order)+1
end rn
from testx, table(generator(rowcount=>1))
where c_condition='or'
)
select x.c_order, x.c_condition,
case
when x.c_order = w.c_order
then w.rn
when x.c_order > (select min(c_order) from temp_cte)
then (select max(rn) from temp_cte where c_order < x.c_order)
else 1
end seq1
from testx x left join temp_cte w
on x.c_order = w.c_order
order by x.c_order;
Output -
C_ORDER
C_CONDITION
SEQ1
1
and
1
2
and
1
3
or
2
4
or
3
5
and
3
6
and
3
7
or
4
8
and
4
9
or
5
For data-set
select * from testx;
C_ORDER
C_CONDITION
1
and
2
and
3
or
4
or
5
and
6
and
7
or
8
and
9
or
Or, just use CONDITIONAL_TRUE_EVENT. Refer
with data(C_ORDER,C_CONDITION) as(
select * from values
(1,'and'),
(2,'and'),
(3,'or'),
(4,'or'),
(5,'and'),
(6,'and'),
(7,'or'),
(8,'and'),
(9,'or')
)select c_order, c_condition,
conditional_true_event(c_condition='or') over (order by c_order) grp
from data;
C_ORDER
C_CONDITION
GRP
1
and
0
2
and
0
3
or
1
4
or
2
5
and
2
6
and
2
7
or
3
8
and
3
9
or
4

Related

What is the most efficient SQL query to find the max N values for every entities in a table

I wrote these 2 queries, the first one is keeping duplicates and the second one is dropping them
Does anyone know a more efficient way to achieve this?
Queries are for MSSQL, returning the top 3 values
1-
SELECT TMP.entity_id, TMP.value
FROM(
SELECT TAB.entity_id, LEAD(TAB.entity_id, 3, 0) OVER(ORDER BY TAB.entity_id, TAB.value) AS next_id, TAB.value
FROM mytable TAB
) TMP
WHERE TMP.entity_id <> TMP.next_id
2-
SELECT TMP.entity_id, TMP.value
FROM(
SELECT TMX.entity_id, LEAD(TMX.entity_id, 3, 0) OVER(ORDER BY TMX.entity_id, TMX.value) AS next_id, TMX.value
FROM(
SELECT TAB.entity_id, LEAD(TAB.entity_id, 1, 0) OVER(ORDER BY TAB.entity_id, TAB.value) AS next_id, TAB.value, LEAD(TAB.value, 1, 0) OVER(ORDER BY TAB.entity_id, TAB.value) AS next_value
FROM mytable TAB
) TMX
WHERE TMP.entity_id <> TMP.next_id OR TMX.value <> TMX.next_value
) TMP
WHERE TMP.entity_id <> TMP.next_id
Example:
Table:
entity_id value
--------- -----
1 9
1 11
1 12
1 3
2 25
2 25
2 5
2 37
3 24
3 9
3 2
3 15
Result Query 1 (25 appears twice for entity_id 2):
entity_id value
--------- -----
1 9
1 11
1 12
2 25
2 25
2 37
3 9
3 15
3 24
Result Query 2 (25 appears only once for entity_id 2):
entity_id value
--------- -----
1 9
1 11
1 12
2 5
2 25
2 37
3 9
3 15
3 24
You can use the ROW_NUMBER which will allow duplicates as follows:
select entity_id, value from
(select t.*, row_number() over (partition by entity_id order by value desc) as rn
from your_Table) where rn <= 3
You can use the rank to remove the duplicate as follows:
select distinct entity_id, value from
(select t.*, rank() over (partition by entity_id order by value desc) as rn
from your_Table) where rn <= 3

SQL Server : how to write the correct query?

This is my query
SELECT
ROW_NUMBER() OVER (ORDER BY [OtpInfoId] ASC ) AS RowNumber,
[OtpInfoId] ,
[OtpStatusId]
INTO
#TempTable
FROM
RequestOTP.Main.TbOtpStatusHistory
And this is part of the results:
RowNumber OtpInfoId OtpStatusId
----------------------------------
1 1 2
2 1 1
3 1 9
4 1 5
1 2 2
2 2 1
3 2 9
4 2 5
1 3 2
2 3 1
3 3 9
4 3 5
5 3 7
I want write a query to return what OtpInfoId have OtpStatusId with at least one state 7
In this example, the query should return this result:
RowNumber OtpInfoId OtpStatusId
----------------------------------
1 3 2
2 3 1
3 3 9
4 3 5
5 3 7
But I don't know how to write it.
You can use a correlated subquery with a WHERE EXIST clause.
SELECT
ROW_NUMBER() OVER (ORDER BY [OtpInfoId] ASC ) AS RowNumber ,
[OtpInfoId] ,
[OtpStatusId]
INTO #TempTable
FROM RequestOTP.Main.TbOtpStatusHistory osh
WHERE EXISTS (
SELECT 1
FROM RequestOTP.Main.TbOtpStatusHistory
WHERE OtpStatusId = 7 AND OtpInfoId = osh.OtpInfoId
)
One method is to use an EXISTS.
SELECT *
FROM (SELECT row_number() OVER (ORDER BY [OtpInfoId] ASC) RowNumber,
[OtpInfoId],
[OtpStatusId]
FROM [RequestOTP].[Main].[TbOtpStatusHistory]) x
WHERE EXISTS (SELECT *
FROM [RequestOTP].[Main].[TbOtpStatusHistory] y
WHERE y.[OtpInfoId] = x.[OtpInfoId]
AND y.[OtpStatusId] = 7)
INTO #TempTable;
Try this:
SELECT * FROM RequestOTP.Main.TbOtpStatusHistory
WHERE OtpInfoId IN (
SELECT OtpInfoId FROM RequestOTP.Main.TbOtpStatusHistory WHERE OtpStatusId = 7
)
INTO #TempTable;
This subquery returns all the OtpInfoId that have OtpStatusId = 7
Here is one approach:
SELECT
RowNumber,
OtpInfoId,
OtpStatusId
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY [OtpInfoId] ORDER BY OtpInfoId) AS RowNumber,
COUNT(CASE WHEN OtpStatusId = 7 THEN 1 END) OVER (PARTITION BY OtpInfoId) cnt
[OtpInfoId],
[OtpStatusId]
FROM RequestOTP.Main.TbOtpStatusHistory
) t
WHERE cnt > 0
INTO #TempTable;
Note that the row numbering in your expected is not clear, because there is no obvious column which provides that ordering. What is missing is a partition on the OtpInfoId, so I added one.
Please try following script to see if it satisfies your requirement.
create table RequestOTP.Main.TbOtpStatusHistory
(
OtpInfoId int,
OtpStatusId int
)
insert into RequestOTP.Main.TbOtpStatusHistory values
(1,2),(1,1),(1,9),(1,5),(2,2),(2,1),
(2,9),(2,5),(3,2),(3,1),(3,9),(3,5),
(3,7)
;with cte as
(
SELECT
ROW_NUMBER() OVER (partition by OtpInfoId ORDER BY [OtpInfoId] ASC ) AS RowNumber,
[OtpInfoId] ,
[OtpStatusId]
FROM RequestOTP.Main.TbOtpStatusHistory
)
select * from cte
where OtpInfoId in
(select OtpInfoId from RequestOTP.Main.TbOtpStatusHistory where OtpStatusId=7)
/*
RowNumber OtpInfoId OtpStatusId
-------------------- ----------- -----------
1 3 2
2 3 1
3 3 9
4 3 5
5 3 7
*/
Best Regards,
Rachel

Update sort Column linked with group

How can i change the position of one row to change the order
Best to explain with example
I have following table with statuses
Id Name StatusOrder StatusGroup
1 Open 1 1
2 Start 2 1
3 Load 3 1
4 Close 4 1
5 Begin 1 2
6 Open 2 2
7 Close 3 2
I would like to Switch from group one only Status order 2 with 3.
The jump can be more than one row, ex. its also possible that within the same group the order from open moves to status order 3
Sow when i do following select
SELECT * FROM Status WHERE (StatusGroup =1)
Result Set:
Id Name StatusOrder StatusGroup
1 Open 1 1
3 Load 2 1
2 Start 3 1
4 Close 4 1
5 Begin 1 2
6 Open 2 2
7 Close 3 2
I already found example with following article but i do not succeed in it to intgrate that only for one group the order changes
Using a sort order column in a database table
How Can help me?
If correctly understood, here you go:
QUERY
create table #t
(
Id INT,
Name VARCHAR(20),
StatusOrder INT,
StatusGroup INT
)
insert into #t values
(1 ,'Open', 1 , 1),
(2 ,'Start', 2 , 1),
(3 ,'Load', 3 , 1),
(4 ,'Close', 4 , 1),
(5 ,'Begin', 1 , 2),
(6 ,'Open', 2 , 2),
(7 ,'Close', 3 , 2)
;with cte as (
select *, row_number() over(partition by StatusGroup order by Id) rn
from #t
)
select case when StatusOrder = 2 then 3 when StatusOrder = 3 then 2 else Id end as Id,
case when StatusOrder = 2 then 'Load' when StatusOrder = 3 then 'Start' else Name end as Name,
StatusOrder,
StatusGroup
from cte
where rn = id
union all
select Id, Name, StatusOrder, StatusGroup
from cte
where rn <> id
drop table #t
OUTPUT
Id Name StatusOrder StatusGroup
1 Open 1 1
3 Load 2 1
2 Start 3 1
4 Close 4 1
5 Begin 1 2
6 Open 2 2
7 Close 3 2
UPDATE
So if you have table where you need update records you can do something like:
;with cte as (
select *, row_number() over(partition by StatusGroup order by Id) rn
from #t
)
update t
set t.Id = (case when cte.StatusOrder = 2 then 3
when cte.StatusOrder = 3 then 2 else t.Id end),
t.Name = (case when cte.StatusOrder = 2 then 'Load'
when cte.StatusOrder = 3 then 'Start' else t.Name end)
from cte
join #t t on cte.id = t.id
where cte.rn = cte.id

Assign rownumber in SQL grouped on value and n rows per rownumber

I am trying to generate a report with 3 rows per page for each order number using the following SQL.
As you can see from the results the fields Actual & Expected do not match up.
Any help would be appreciated.
set nocount on
DECLARE #Orders TABLE (Expected int, OrderNumber INT, OrderDetailsNumber int)
Insert into #orders values (0,1,1)
Insert into #orders values (0,1,2)
Insert into #orders values (0,1,3)
Insert into #orders values (1,1,4)
Insert into #orders values (2,2,5)
Insert into #orders values (2,2,6)
Insert into #orders values (2,2,7)
Insert into #orders values (3,2,8)
Insert into #orders values (3,2,9)
select cast(((row_number() over( order by OrderNumber)) -1) /3 as int) as [Actual]
,*
from #orders
Actual Expected OrderNumber OrderDetailsNumber
----------- ----------- ----------- ------------------
0 0 1 1
0 0 1 2
0 0 1 3
1 1 1 4
1 2 2 5
1 2 2 6
2 2 2 7
2 3 2 8
2 3 2 9
Right, after a couple of edits I have the final answer:
SELECT DENSE_RANK() OVER (Order BY OrderNumber, floor(RowNumber/3)) - 1 AS Actual,
Expected,
OrderNumber,
OrderDetailsNumber
FROM
(
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY OrderNumber
ORDER BY OrderDetailsNumber
) - 1 AS RowNumber
FROM #Orders
) RowNumberTable
Gives the result (with extra rows for testing):
Actual Expected OrderNumber OrderDetailsNumber
-------------------- ----------- ----------- ------------------
0 0 1 1
0 0 1 2
0 0 1 3
1 1 1 4
1 1 1 12
2 2 2 5
2 2 2 6
2 2 2 7
3 3 2 8
3 3 2 9
3 4 2 11
4 3 2 27
5 5 3 10
This only works where OrderDetailsNumber is unique such that the result is deterministic.
Edit
I've now got the complete code working, however the dependence on OrderDetailsNumber being in order is very iffy, hopefully you can test and edit as required.
Edit 2
I've put the 'golfed' version in the main answer.
WITH FirstCTE AS
(
SELECT
OrderNumber,
OrderDetailsNumber,
Expected,
ROW_NUMBER() OVER (
PARTITION BY OrderNumber
ORDER BY OrderDetailsNumber
) - 1 AS RowNumber
FROM #Orders
)
, SecondCTE AS
(
SELECT OrderDetailsNumber as odn,
floor(RowNumber/3) as page_for_order_number,
DENSE_RANK() OVER (Order BY OrderNumber, floor(RowNumber/3)) - 1 AS Actual
FROM FirstCTE
)
SELECT c2.page_for_order_number,
c1.RowNumber,
C2.Actual,
c1.Expected,
c1.OrderNumber,
c1.OrderDetailsNumber
FROM FirstCTE AS c1
INNER JOIN SecondCTE AS c2
on c2.odn = c1.OrderDetailsNumber
This strikes me as a bit of a hack, but it works...
Divide the row_number() by 3, and use CEILINGto get the smallest integer greater than or equal to the result of that division.
select row_number() over( order by OrderNumber) as [Actual],
cast (row_number() over(order by ordernumber) as decimal(5,1)) / 3,
CEILING(cast (row_number() over(order by ordernumber) as decimal(5,1)) / 3)as GRPR,
*
from #orders
EDIT: Dang it, can never get results to line up. The 3rd column in the result set is your "page number".
Which yields:
Actual (No column name) PG_NBR Expected OrderNumber OrderDetailsNumber
1 0.333333 1 0 1 1
2 0.666666 1 0 1 2
3 1.000000 1 0 1 3
4 1.333333 2 1 1 4
5 1.666666 2 2 2 5
6 2.000000 2 2 2 6
7 2.333333 3 2 2 7
8 2.666666 3 3 2 8
9 3.000000 3 3 2 9

Select and aggregate last records base on order

I have different versions of the charges in a table. I want to grab and sum the last charge grouped by Type.
So I want to add 9.87, 9.63, 1.65.
I want the Parent ID , sum(9.87 + 9.63 + 1.65) as the results of this query.
We use MSSQL
ID ORDER CHARGES TYPE PARENT ID
1 1 6.45 1 1
2 2 1.25 1 1
3 3 9.87 1 1
4 1 6.54 2 1
5 2 5.64 2 1
6 3 0.84 2 1
7 4 9.63 2 1
8 1 7.33 3 1
9 2 5.65 3 1
10 3 8.65 3 1
11 4 5.14 3 1
12 5 1.65 3 1
WITH recordsList
AS
(
SELECT Type, Charges,
ROW_NUMBER() OVER (PArtition BY TYPE
ORDER BY [ORDER] DESC) rn
FROM tableName
)
SELECT SUM(Charges) totalCharge
FROM recordsLIst
WHERE rn = 1
SQLFiddle Demo
Use row_number() to identify the rows to be summed, and then sum them:
select SUM(charges)
from (select t.*,
ROW_NUMBER() over (PARTITION by type order by id desc) as seqnum
from t
) t
where seqnum = 1
Alternatively you could use a window aggregate MAX():
SELECT SUM(Charges)
FROM (
SELECT
[ORDER],
Charges,
MaxOrder = MAX([ORDER]) OVER (PARTITION BY [TYPE])
FROM atable
) s
WHERE [ORDER] = MaxOrder
;
SELECT t.PARENT_ID, SUM(t.CHARGES)
FROM dbo.test73 t
WHERE EXISTS (
SELECT 1
FROM dbo.test73
WHERE [TYPE] = t.[TYPE]
HAVING MAX([ORDER]) = t.[ORDER]
)
GROUP BY t.PARENT_ID
Demo on SQLFiddle