how to make Numbering rows in a query - sql

I have a simple select statement:
Get numbering of rows from Product table in the following order: a name of the manufacturer in
--decreasing order of quantity of models produced by it (when there is identical quantity of models
--for a number of manufacturers, their names should follow in increasing alphabetic order), model (increasing order).
--Output: number in accordance with the above order, a name of the manufacturer (maker), model
RESULT SHOULD BE -
no maker model
1 A 1232
10 E 2112
11 E 2113
12 B 1121
13 B 1750
14 D 1288
15 D 1433
16 C 1321
2 A 1233
3 A 1276
4 A 1298
5 A 1401
6 A 1408
7 A 1752
8 E 1260
9 E 1434
MY QUERY :
with result as
(select *, count(*) over (partition by maker) as ModelsCount from Product)
select row_number() over (order by ModelsCount desc,model) AS No, Maker, Model
from result
MY RESULT:
no maker model
1 A 1232
10 E 2112
11 E 2113
12 B 1121
13 D 1288
14 D 1433
15 B 1750
16 C 1321
2 A 1233
3 A 1276
4 A 1298
5 A 1401
6 A 1408
7 A 1752
8 E 1260
9 E 1434

Related

looking for values from another table where they do not exist in a given group

I have two tables:
SHOPPING
date
id_customer
id_shop
id_fruit
28.03.2018
7423
123
1
13.02.2019
8408
354
1
28.03.2019
7767
123
9
13.02.2020
8543
472
7
28.03.2020
8640
346
9
13.02.2021
7375
323
9
28.03.2021
7474
323
8
13.02.2022
7476
499
1
28.03.2022
7299
123
4
13.02.2023
8879
281
2
28.03.2023
8353
452
1
13.02.2024
8608
499
6
28.03.2024
8867
318
1
13.02.2025
7997
499
6
28.03.2025
7715
499
4
13.02.2026
7673
441
7
FRUITS
id_fruit
name
1
apple
2
pear
3
grape
4
banana
5
plum
6
melon
7
watermelon
8
orange
9
pineapple
I would like to find fruits that have never been bought in a specific id_shop
I tried with this:
SELECT
s.idshop,
s.id_fruit ,
f.name
FROM
shopping s
LEFT JOIN fruit f ON f.id_fruit = s.id_fruit
WHERE NOT EXISTS (
SELECT *
FROM
fruit f1
WHERE f1.id_fruit = s.id_fruit
)
but it does not work...
Yes, you need an OUTER JOIN, but that should be RIGHT JOIN along with NULL values picked from shopping table after join applied, considering your current query such as
SELECT f.*
FROM shopping s
RIGHT JOIN fruit f
ON f.id_fruit = s.id_fruit
WHERE s.id_fruit IS NULL
Demo

How to Properly Write SELECT Statements When Dealing with Multiple Tables

To demonstrate what I am asking, I will present two tables: The Product table and the PC table. The product table contains the following information:
MAKER MODEL TYPE
----- ---------- -------
A 1232 PC
A 1233 PC
A 1276 Printer
A 1298 Laptop
A 1401 Printer
A 1408 Printer
A 1752 Laptop
B 1121 PC
B 1750 Laptop
C 1321 Laptop
D 1288 Printer
D 1433 Printer
E 1260 PC
E 1434 Printer
E 2111 PC
E 2112 PC
The PC table contains the following information:
CODE MODEL SPEED RAM HD CD PRICE
---------- ---------- ---------- ---------- ---------- ---------- ----------
1 1232 500 64 5 12x 600
2 1121 750 128 14 40x 850
3 1233 500 64 5 12x 600
4 1121 600 128 14 40x 850
5 1121 600 128 8 40x 850
6 1233 750 128 20 50x 950
7 1232 500 32 10 12x 400
8 1232 450 64 8 24x 350
9 1232 450 32 10 24x 350
10 1260 500 32 10 12x 350
11 1233 900 128 40 40x 980
12 1233 800 128 20 50x 970
In order to execute a query that returns all PC models, which of the following queries would be better:
SELECT model FROM pc;
OR
SELECT model FROM product JOIN pc USING(model);
Since you only need PC models, the first query you stated will be OK to use.
In your second query, you are getting the product table content first then joining them into PC table, which is slower than your first query and since you do not need any column from Product table, it is not necessary.
You can go with the query below as an alternative, to return only PC related rows, if you do not add the where clause and there is a PC model as 1276. your second query would return the A - 1276 - Printer row.
SELECT DISTINCT model FROM product JOIN pc USING(model) where product.type='PC';

SQL Count for Each Category

I need to count rows based on 2 fields for grouping.
Animals (a)
id group_id strain_id death_date death_cause status
-----------------------------------------------------------------------
1 512 164 2015-12-01 Culled P
2 512 164 2015-12-02 Culled A
3 512 164 2015-12-02 Surplus B
4 512 230 2015-12-06 Culled A
5 512 164 2015-12-28 Culled A
6 512 230 2016-01-20 Culled B
7 512 230 2016-01-20 Surplus P
8 512 164 NULL NULL P
9 512 230 NULL NULL B
10 512 230 NULL NULL A
11 512 164 2016-01-25 Culled B
12 512 164 2016-02-29 Culled A
13 512 230 2016-02-03 Surplus P
14 512 230 2016-02-03 Culled A
Groups (g)
id group_name
--------------
512 Mice
Strain (s)
id strain_name
----------------
164 Strain 1
230 Strain 2
Group Animal Count (gac)
id total_animals alive_count dead_count
----------------------------------------------------------------------
512 14 3 11
Mating History (mh)
id animal_id history_type history_date
--------------------------------------------------------
1001 2 MA 2015-11-20
1002 2 MR 2015-12-01
1003 3 MA 2015-12-01
1004 6 FA 2015-12-21
1005 9 FA 2016-02-07
1006 10 MA 2016-01-27
1007 11 FA 2015-12-12
So when I group them by the strain_id and the death_cause this is what they should look like visually:
Strain 1 ---- Culled
1 512 164 2015-12-01 Culled P
2 512 164 2015-12-02 Culled A
5 512 164 2015-12-28 Culled A
11 512 164 2016-01-25 Culled B
12 512 164 2016-02-29 Culled A
Strain 1 ---- Surplus
3 512 164 2015-12-02 Surplus B
Strain 2 ---- Culled
4 512 230 2015-12-06 Culled A
6 512 230 2016-01-20 Culled B
14 512 230 2016-02-03 Culled A
Strain 2 ---- Surplus
7 512 230 2016-01-20 Surplus P
13 512 230 2016-02-03 Surplus P
What I want to get from the SQL query is the following result:
g_name s_name d_cause a_total c_alive c_dead c_pup c_breeder c_total
------------------------------------------------------------------------------
Mice Strain 1 Culled 12 3 9 1 2 5
Mice Strain 1 Surplus 12 3 9 0 1 1
Mice Strain 2 Culled 12 3 9 0 1 3
Mice Strain 2 Surplus 12 3 9 2 0 2
Basically I want to count the number of animals using 2 categories which in this case is the strain_name and the death_cause
Note that for an animal to be counted as a breeder (c_breeder), I have to look at the Mating History table and check if the animal_id has ever had any of these codes MA or FA.
I am using INNER JOIN on the groups, group_animal_count, and strains. I use LEFT JOIN for mating_history since animals with a status of P won't have records in that table since they're just pups and won't be involved with mating.
Try:
SELECT group_name, strain_name,death_cause, count(*) as Total
FROM ANIMALS a
JOIN GROUPS g ON a.group_id = g.id
JOIN STRAIN s ON a.strain_id = s.id
GROUP BY group_name, strain_name,death_cause
You can do the aggregation before joining the tables:
SELECT group_name, strain_name, death_cause, total
FROM (
SELECT group_id,
strain_id,
death_cause,
COUNT(*) AS total
FROM animals
GROUP BY group_id, strain_id, death_cause
) a
INNER JOIN groups g ON ( g.group_id = a.group_id )
INNER JOIN strain s ON ( s.strain_id = a.strain_id );

TSQL Interleaved sequence without join operations or user defined objects

Is that possible to create an interleaved sequence with first and last values going in turns untill data set is empty without using joins and user defined functions or procedures?
code type model price
1 Hats 1298 700,00
1 Shoes 1232 600,00
1 Pants 1276 400,00
2 Hats 1321 970,00
2 Shoes 1121 850,00
2 Pants 1433 270,00
3 Hats 1750 1200,00
3 Shoes 1233 600,00
3 Pants 1434 290,00
4 Hats 1298 1050,00
4 Shoes 1121 850,00
4 Pants 1401 150,00
5 Hats 1752 1150,00
5 Shoes 1121 850,00
5 Pants 1408 270,00
6 Hats 1298 950,00
6 Shoes 1233 950,00
6 Pants 1288 400,00
7 Shoes 1232 400,00
8 Shoes 1232 350,00
9 Shoes 1232 350,00
10 Shoes 1260 350,00
11 Shoes 1233 980,00
12 Shoes 1233 970,00
I have added extra spaces between rows to get the interleaved sequence idea.
You want to get the odd values going from lowest coded items (asc) and even values with the highest coded items (desc). You also want to order type by Hats,Shoes and Pants.
code type model price
1 Hats 1298 700,00
1 Shoes 1232 600,00
1 Pants 1276 400,00
6 Hats 1298 950,00
12 Shoes 1233 970,00
6 Pants 1288 400,00
2 Hats 1321 970,00
2 Shoes 1121 850,00
2 Pants 1433 270,00
5 Hats 1752 1150,00
11 Shoes 1233 980,00
5 Pants 1408 270,00
3 Hats 1750 1200,00
3 Shoes 1233 600,00
3 Pants 1434 290,00
4 Hats 1298 1050,00
10 Shoes 1260 350,00
4 Pants 1401 150,00
4 Shoes 1121 850,00
9 Shoes 1232 350,00
5 Shoes 1121 850,00
8 Shoes 1232 350,00
6 Shoes 1233 950,00
7 Shoes 1232 400,00
Right now I came up with solution that includes joins but I am looking for something that would work without using it.
My solution with using joins:
with cteasc as
(
select
ROW_NUMBER() over(order by code,charindex(type, 'HatsShoesPants'))id
,(ROW_NUMBER() over(partition by type order by code,charindex(type, 'HatsShoesPants')) + 1) / 2 offsetasc
,code,model,price,type
from mydata
),
ctedsc as
(
select
ROW_NUMBER() over (partition by type order by code desc)id
,code,model,price,type
from cteasc
)
select t1.id
,case
when t1.code%2=1
then LAG(t1.type,t1.code-t1.offsetasc,t1.type)over(partition by t1.type order by t1.id)
else LAG(t2.type,t1.code-t1.offsetasc,t1.type)over(partition by t1.type order by t1.id)
end type
,case
when t1.code%2=1
then LAG(t1.model,t1.code-t1.offsetasc,t1.model)over(partition by t1.type order by t1.id)
else LAG(t2.model,t1.code-t1.offsetasc,t1.model)over(partition by t1.type order by t1.id)
end model
,case
when t1.code%2=1
then LAG(t1.price,t1.code-t1.offsetasc,t1.price)over(partition by t1.type order by t1.id)
else LAG(t2.price,t1.code-t1.offsetasc,t1.price)over(partition by t1.type order by t1.id)
end price
from
cteasc t1
join ctedsc t2
on t1.code = t2.id
and t1.type = t2.type
The idea would be to generate two sequences:
The ascending items with row numbers 1, 3, 5, ...
The descending items with row numbers 2, 4, 6, ...
Then, we simply need to UNION ALL the sequences and sort them.
with ascItems as (
select *, row_number() over (order by someCol ASC) r * 2 - 1 as r from T
)
, descItems as (
select *, row_number() over (order by someCol DESC) r * 2 as r from T
)
select * from ascItems
union all
select * from descItems
order by r
This should require the table T to be available sorted twice (either by index or by 2xsorting). The UNION ALL should manifest itself as a cheap merge concat.

Complicated TSQL custom ranking task

I have tried a whole lot of variety of ranking solutions with joins and all to match the needs I want.
Sadly, I cannot come up with correct query to get the desired output.
I am really looking for any help to get explanation that would help me in future with these sort of tasks.
I have the following CTE table contains data values set:
type model price code
Shoes 1298 700,00 1
Shoes 1298 950,00 6
Shoes 1298 1050,00 4
Shoes 1321 970,00 2
Shoes 1750 1200,00 3
Shoes 1752 1150,00 5
Pants 1121 850,00 2
Pants 1121 850,00 4
Pants 1121 850,00 5
Pants 1232 350,00 8
Pants 1232 350,00 9
Pants 1232 400,00 7
Pants 1232 600,00 1
Pants 1233 600,00 3
Pants 1233 950,00 6
Pants 1233 970,00 12
Pants 1233 980,00 11
Pants 1260 350,00 10
Hats 1276 400,00 1
Hats 1288 400,00 6
Hats 1401 150,00 4
Hats 1408 270,00 5
Hats 1433 270,00 2
Hats 1434 290,00 3
Unified records numbering of CTE have to be done in the following manner: first there are the first models of the tables (Shoes, Pants and Hats), then the last models, after that - the second models in the tables, the penultimate, etc. In the case of exhaustion of the models of a particular type, number only remaining models of other types.
Here is the desired output:
Id type model price code
1 Shoes 1298 700.0000 1
2 Pants 1232 600.0000 1
3 Hats 1276 400.0000 1
4 Shoes 1298 950.0000 6
5 Pants 1233 970.0000 12
6 Hats 1288 400.0000 6
7 Shoes 1321 970.0000 2
8 Pants 1121 850.0000 2
9 Hats 1433 270.0000 2
10 Shoes 1752 1150.0000 5
11 Pants 1233 980.0000 11
12 Hats 1408 270.0000 5
13 Shoes 1750 1200.0000 3
14 Pants 1233 600.0000 3
15 Hats 1434 290.0000 3
16 Shoes 1298 1050.0000 4
17 Pants 1260 350.0000 10
18 Hats 1401 150.0000 4
19 Pants 1121 850.0000 4
20 Pants 1232 350.0000 9
21 Pants 1121 850.0000 5
22 Pants 1232 350.0000 8
23 Pants 1233 950.0000 6
24 Pants 1232 400.0000 7
I have updated the desired output (added code column) to better understand the sorting idea. It has to be done in interleaved manner with first coded numbers(i.e. lowest code) of types goes first then the last coded numbers(i.e. highest code) of types goes second, then first coded who's left goes first and then the last coded who's left and etc.
I can not really understand the ordering behind the scene, because the result set is not ordered nor by model nor by code, but here is the idea and you can play with orderings in CTEs:
WITH cte1 AS ( SELECT * ,
ROW_NUMBER() OVER (PARTITION BY type ORDER BY model, code) rn1
FROM #t),
cte2 AS ( SELECT * ,
ROW_NUMBER() OVER (PARTITION BY rn1 ORDER BY
CASE type WHEN 'Shoes' THEN 1
WHEN 'Pants' THEN 2
WHEN 'Hats' THEN 3 END) rn2
FROM cte1 )
SELECT * ,
ROW_NUMBER() OVER (ORDER BY rn1, rn2) rn
FROM cte2
Output:
type model price code rn1 rn2 rn
Shoes 1298 700.00 1 1 1 1
Pants 1121 850.00 2 1 2 2
Hats 1276 400.00 1 1 3 3
Shoes 1298 1050.00 4 2 1 4
Pants 1121 850.00 4 2 2 5
Hats 1288 400.00 6 2 3 6
Shoes 1298 950.00 6 3 1 7
Pants 1121 850.00 5 3 2 8
Hats 1401 150.00 4 3 3 9
Shoes 1321 970.00 2 4 1 10
Pants 1232 600.00 1 4 2 11
Hats 1408 270.00 5 4 3 12
Shoes 1750 1200.00 3 5 1 13
Pants 1232 400.00 7 5 2 14
Hats 1433 270.00 2 5 3 15
Shoes 1752 1150.00 5 6 1 16
Pants 1232 350.00 8 6 2 17
Hats 1434 290.00 3 6 3 18
Pants 1232 350.00 9 7 1 19
Pants 1233 600.00 3 8 1 20
Pants 1233 950.00 6 9 1 21
Pants 1233 980.00 11 10 1 22
Pants 1233 970.00 12 11 1 23
Pants 1260 350.00 10 12 1 24
Rn contains desired values.
You want the value interleaved. Here is how you can do this:
with cte as ( . . . )
select row_number() over (order by seqnum, charindex(type, 'ShoesPantsHats')) as id,
t.*
from (select cte.*,
row_number() over (partition by type order by (select NULL)) as seqnum
from cte
) t
order by seqnum,
charindex(type, 'ShoesPantsHats');
Note the order by clause for row_number(). SQL tables are inherently unordered, and if you care about the ordering of the results within each type, then put the appropriate logic there.
Try this,
with cte as (select type, model,price, ROW_NUMBER() over (partition by type order by type) as rowid from temp)
select * from cte order by rowid, type