Return 1 row from all groups - sql

https://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=9e6f83edf836f4496afb509eb9411d4a
Edited to include sql code:
CREATE TABLE TMP_PRODUCTS (STORE INT, UPC INT, PROMOCODE CHAR(3), FORSALE CHAR(1))
INSERT INTO TMP_PRODUCTS VALUES
(100,1,'123','Y'),
(100,2,'123','Y'),
(100,3,'123','N'),
(100,4,'124','Y'),
(100,5,'124','N'),
(100,6,'124','N'),
(100,7,'125','N'),
(100,8,'125','N'),
(100,9,'125','N');
SELECT
STORE,
UPC,
PROMOCODE,
DENSE_RANK() OVER (PARTITION BY STORE ORDER BY PROMOCODE) AS 'GroupCode'
FROM
TMP_PRODUCTS
WHERE
FORSALE = 'Y'
I need to return all rows where FORSALE='Y' across all groups of PROMOCODE, and also at least 1 row from all groups where FORSALE='N'. In this example all products from group 125 are FORSALE='N', but I need at least 1 row to return. Here is the output I am currently getting:
STORE UPC PROMOCODE GroupCode FORSALE
100 1 123 1 Y
100 2 123 1 Y
100 4 124 2 Y
But here is the ideal output I would like to get:
STORE UPC PROMOCODE GroupCode FORSALE
100 1 123 1 Y
100 2 123 1 Y
100 4 124 2 Y
100 7 125 3 N
It would also be completely acceptable to return 1 row from PROMOCODE 123 and 124 even though they already have some items that are FORSALE='Y'. So this would also be acceptable outcome:
STORE UPC PROMOCODE GroupCode FORSALE
100 1 123 1 Y
100 2 123 1 Y
100 3 123 1 N
100 4 124 2 Y
100 5 124 2 N
100 7 125 3 N

You can do that with an additional row number window function to always include 1 row from each group regardless of Y/N
select STORE, UPC, PROMOCODE, Dense_Rank() over (partition by STORE order by PROMOCODE) GROUPCODE, FORSALE
from (
select * , Row_Number() over(partition by STORE, PROMOCODE order by UPC) rn
from TMP_PRODUCTS
)x
where FORSALE = 'Y' or rn=1

If I understand correctly, the logic you want is:
SELECT STORE, UPC, PROMOCODE,
DENSE_RANK() OVER (PARTITION BY STORE ORDER BY PROMOCODE) AS GroupCode
FROM (SELECT P.*,
ROW_NUMBER() OVER (PARTITION BY STORE, PROMOCODE, FORSALE ORDER BY (SELECT NULL)) as seqnum
FROM TMP_PRODUCTS P
) P
WHERE FORSALE = 'Y' OR seqnum = 1;

Related

Oracle SQL: How to add a col to assign a seq no. but only 1 and 2 (per unique id)

Is there a way to assign a value ("IdNo") sequentially, 1 and 2, per CustNo? Below is my query (or I can put this query in a view), but not sure how to add an "IdNo" column that will just assign a "1" and "2" respectively, per unique CustNo.
with row as
(select z.*,
row_number ()
over (partition by CustNo
order by FoodDate desc) rn
from table z)
select CustNo,
Food,
FoodDate
from row
where rn <= 2
order by CustNo, FoodDate desc
Below is what I want it to look like with the IdNo added...
IdNo CustNo Food FoodDate
1 101 Red-Apple 7/5/22
2 101 Red-Apple 7/5/22
1 256 Red-Apple 7/11/22
2 256 Red-Cherry 5/20/22
1 418 Blue-Muffin 4/1/22
2 418 Blue-Berry 3/16/22
1 599 Orange-Persimmon 2/8/22
2 599 Red-Apple 1/23/22
1 654 Blue-Berry 12/4/21
2 654 Yellow-Banana 11/27/21
Just add rn on your query.
with row as
(select z.*,
row_number ()
over (partition by CustNo
order by FoodDate desc) rn
from table z)
select rn as IdNo,
CustNo,
Food,
FoodDate
from row
where rn <= 2
order by CustNo, FoodDate desc

DENSE_RANK() Query

I have something similar to the below dataset...
ID RowNumber
101 1
101 2
101 3
101 4
101 5
101 1
101 2
What I would like to get is an additional column as below...
ID RowNumber New
101 1 1
101 2 1
101 3 1
101 4 1
101 5 1
101 1 2
101 2 2
I have toyed with dense_rank(), but no such luck.
Gordon already mentioned, you required a column to specify the order of data. If i consider ID as order by column, this following logic may help you to get your desired result-
WITH your_table(ID,RowNumber)
AS
(
SELECT 101,1 UNION ALL
SELECT 101,2 UNION ALL
SELECT 101,3 UNION ALL
SELECT 101,4 UNION ALL
SELECT 101,5 UNION ALL
SELECT 101,1 UNION ALL
SELECT 101,2
)
SELECT A.ID,A.RowNumber,
SUM(RN) OVER
(
ORDER BY ID
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) +1 New
FROM
(
SELECT *,
CASE
WHEN LAG(RowNumber) OVER(ORDER BY ID) > RowNumber THEN 1
ELSE 0
END RN
FROM your_table
)A
Above will always change the ROW NUMBER if the value in RowNumber decreased than previous one. Alternatively, the same output alsoo can be achieved if you wants to change row number whenever value 1 found. This is bit static option-
SELECT A.ID,A.RowNumber,
SUM(RN) OVER
(
ORDER BY ID
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) New
FROM(
SELECT *,
CASE
WHEN RowNumber = 1 THEN 1
ELSE 0
END RN
FROM your_table
)A
Output is-
ID RowNumber New
101 1 1
101 2 1
101 3 1
101 4 1
101 5 1
101 1 2
101 2 2
SQL tables represent unordered sets. There is no ordering unless a column specifies the ordering.
Assuming you have such a column, you can do what you want simply by counting the number of "1" up to each point:
select t.*,
sum(case when rownumber = 1 then 1 else 0 end) over (partition by id order by <ordering column>) as new
from t;
As Gordon alluded to, there is no default order in your example so it's difficult to imagine how to get a deterministic result (e.g. where the same values supplied to the same query always resulting in the exact same answer.)
This sample data includes a sequential PK column which is used to define the order of this set.
DECLARE #tbl TABLE (PK INT IDENTITY, ID INT, RowNumber INT)
INSERT #tbl(ID, RowNumber) VALUES (101,1),(101,2),(101,3),(101,4),(101,5),(101,1),(101,2);
SELECT t.* FROM #tbl AS t;
Returns:
PK ID RowNumber
----- ------ -----------
1 101 1
2 101 2
3 101 3
4 101 4
5 101 5
6 101 1
7 101 2
This query uses DENSE_RANK to get you what you want:
DECLARE #tbl TABLE (PK INT IDENTITY, ID INT, RowNumber INT)
INSERT #tbl(ID, RowNumber) VALUES (101,1),(101,2),(101,3),(101,4),(101,5),(101,1),(101,2);
SELECT t.ID, t.RowNumber, New = DENSE_RANK() OVER (ORDER BY t.PK - RowNumber)
FROM #tbl AS t;
Returns:
ID RowNumber New
----- ----------- ------
101 1 1
101 2 1
101 3 1
101 4 1
101 5 1
101 1 2
101 2 2
Note that ORDER BY New does not affect the plan.
Please try the below
Load the data into Temp table
Select id,RowNumber,Row_number()over(partition by RowNumber Order by id)New from #temp
Order by Row_number()over(partition by RowNumber Order by id),RowNumber

How do I add conditions to a row number over partition by statement

I am trying to assign a top store to clients based on total sales, and then just by country sales.
I realize i could probably get this if I create a new sales field but just trying to save time. Thoughts?
See below for the code i am using
select client_id
,region
,country
,row_number() over(partition by clientid order by sales desc) as rank
,case when region='US' then row_number() over(partition by sas_id order by sales desc) else 0 end as usrank
ClientID Region Store sales Rank USRank1 USRank 2
A US 20 100 2 2 1
B US 30 5 6 6 2
C CA 100 20 4 0 0
D CA 120 10 5 0 0
E MX 200 300 1 0 0
F MX 230 50 3 0 0
You need a sum(), grouped by country in the second case.
SELECT client_id,
region,
country,
ROW_NUMBER () OVER (PARTITION BY clientid ORDER BY sales DESC) AS RANK,
sum (sales) OVER (PARTITION BY clientid ORDER BY sales DESC) as CNTRY_RANK

How to use this in sql -- > max(sum (paid * quantity )) to solve a query

How to get the max value order of each customer ?
select num, max(sum(paid*quantity))
from orders join
pizza
using (order#)
group by customer#;
table
num orderN price
-------- --- -------
1 109 30
1 118 25
3 101 30
3 115 27
4 107 23
5 100 17
5 129 16
output req-
num Pnum price
-------- --- -------
1 109 30
3 101 30
4 107 23
5 100 17
You want to select the record having the highest price in each group of nums.
If your RDBMS supports window functions, that's straight forward with ROW_NUMBER() :
SELECT num, pnum, price
FROM (
SELECT t.*, ROW_NUMBER OVER(PARTITION BY num ORDER BY price DESC) rn
FROM mytable t
) x
WHERE rn = 1
Else, you can take the following approach, that uses a NOT EXISTS condition with a correlated subquery to ensure that the record being joined in the one with the highest price for the current num :
SELECT num, pnum, price
FROM mytable t
WHERE NOT EXISTS (
SELECT 1 FROM mytable t1 WHERE t1.num = t.num AND t1.price > t.price
)

Need to select 1 row from each group in sql

am trying to do this query. This is what I have.
My table is: Table
QId InternalId type. priority userid
100 100 1 0 X101
100 100 1 1 X102
100 100 2 0 X103
100 100 2 0 X104
100 100 2 0 X105
100 100 3 0 X106
100 100 3 0 X107
101 101 2 1 X114
101 101 2 0 X115
101 101 3 0 X116
101 101 3 0 X117
For QId and InternalId we have type 1,2,3. I need 1 row for each group based type. Here condition is if priority is 1 then we need take that record. if priority is not set need to take first record.
I need result like below table
QId InternalId type. priority userid
100 100 1 1 X102
100 100 2 0 X103
100 100 3 0 x106
101 101 2 1 X114
101 101 3 0 X116
Can you please help me out in this
Just use row_number():
select t.*
from (select t.*,
row_number() over (partition by QId, InternalId, type order by (select null)) as seqnum
from t
) t
where seqnum = 1;
You can try using row_number()
select * from
(
select *, row_number() over(partition by qid, internalid, type order by priority desc) as rn
from tablename
)A where rn=1
use row_number() window function
select t.* from (select *,row_number()over(partition by QId,InternalId,type order by case when priority=1 then 1 else 2 end) rn
) where t.rn=1
Another approach can be WITH TIES like following.
select top 1 with ties *
from #table
order by row_number() over (partition by QId, InternalId, type order by (select 1))
Online Demo