TSQL Interleaved sequence without join operations or user defined objects - sql

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.

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

Get all the orders that were purchased along with group of certain items in SQL

I would like to know how will I be able to Get all the orders that were purchased along with group of certain items. I have a master items table where it contains around 90 Based Items and I have My order table that contains all the receipts. so the output show shows all the orders that were purchased along with my based items in my Item Table.
Master Item Table:
ItemCode
-------
10001
10002
10003
10004
Order Table:
SiteID BusinessDate ItemName Units Sold Units Sale ItemCode OrderNo
----------------------------------------------------------------------------
1 06/08/2018 Apple 1 5 10001 122-1
1 06/08/2018 Coffee 1 16 10002 122-1
1 06/08/2018 Ice Tea 2 7 10044 122-1
1 06/08/2018 Beans 9 18 10004 122-1
4 06/08/2018 Donuts 7 17 10066 122-7
1 06/08/2018 Bread 1 7 10003 122-4
1 06/08/2018 Beans 4 8 10004 122-4
2 06/08/2018 Apple 2 5 10001 122-2
2 06/08/2018 Coffee 1 6 10002 122-2
3 06/08/2018 Bread 3 5 10003 122-3
3 06/08/2018 Beans 7 17 10004 122-3
3 06/08/2018 Ice Tea 7 17 10044 122-5
4 06/08/2018 Ice Coffee 7 17 10050 122-6
Result:
SiteID BusinessDate ItemName Units Sold Units Sale ItemCode OrderNo
----------------------------------------------------------------------------
1 06/08/2018 Apple 1 5 10001 122-1
1 06/08/2018 Coffee 1 16 10002 122-1
1 06/08/2018 Ice Tea 2 7 10044 122-1
1 06/08/2018 Beans 9 18 10004 122-1
1 06/08/2018 Bread 1 7 10003 122-4
1 06/08/2018 Beans 4 8 10004 122-4
2 06/08/2018 Apple 2 5 10001 122-2
2 06/08/2018 Coffee 1 6 10002 122-2
3 06/08/2018 Bread 3 5 10003 122-3
3 06/08/2018 Beans 7 17 10004 122-3
the query output has to return to me all the transaction that are purchased along with my Item master table values only, for example the output query didn't return the Order No: 122-6 as the Item Code not present in my master item code while Order No:122-1 is shown because one or two of my ItemCode are present in the order & Master Item table
You can try to write a subquery, which OrderNo and SiteID is matched.
then do join to [Order] table to get your expect result.
SELECT o.*
FROM [Order] o JOIN
(
SELECT DISTINCT OrderNo,SiteID
FROM MasterItem m
JOIN [Order] o on o.ItemCode = m.ItemCode
) t1 on t1.OrderNo = o.OrderNo and t1.SiteID = o.SiteID
sqlfiddle
Something like this? (substitute your own table names)
select * from [Order]
where [OrderNo] in (
select distinct [OrderNo]
from [Order] o
inner join [MasterItem] m on o.[ItemCode] = m.[ItemCode]
)
The subquery gets a list of orders that have the desired items in it, then this is used to select all rows from the Orders table. In other words, this will return all Order rows (any item) where there is an item in the order that is in the list of items in the MasterItem table.
Use an inner join
SELECT * FROM Order o
JOIN Item i ON i.itemCode = o.itemCode
Use inner join:
select * from ordertable o
inner join mastertable m on o.itemcode=m.itemcode
I wold use exists
select * from [Order] o
where exists(select 1 from
(SELECT DISTINCT OrderNo,SiteID
FROM MasterItem m
JOIN [Order] o on o.ItemCode = m.ItemCode
) t
where t.OrderNo=o.OrderNo and t.SiteID=o.SiteID
)

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

how to make Numbering rows in a query

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

How to find all (group-based) rows containing MIN/MAX values for a particular column in SQL Server?

I have temporary table (#TempPackages) which looks like this:
EntryId (PK) PackageId SubProductID SubProductSequence
1 1111 17 3
2 1111 28 4
3 1111 33 1
4 1111 67 5
5 1111 122 2
6 2222 18 4
7 2222 29 5
8 2222 33 9
9 2222 103 7
10 2222 99 11
11 3333 256 5
12 3333 333 6
13 3333 789 3
14 3333 1023 2
15 3333 9845 1
I need a query which will give me the rows with the minimum/maximum SubProductSequence value for each unique PackageId. For the table above, the query would return this:
EntryId (PK) PackageId SubProductID SubProductSequence
3 1111 33 1
4 1111 67 5
6 2222 18 4
10 2222 99 11
12 3333 333 6
15 3333 9845 1
The EntryId column was something that I added while trying to solve this, as it gives me a unique column to join the same table on to (to ensure I still only end up with 15 rows in my joined table).
I tried this - just to get the MIN():
SELECT
*
FROM
#TempPackages p1
INNER JOIN
#TempPackages p2 ON p1.EntryId = p2.EntryId
AND p1.SubProductSequence = (
SELECT
MIN(SubProductSequence)
FROM
#DeparturesToUpdate)
Obviously this is wrong, because the INNER JOIN is superfluous and the SELECT MIN() clause is wrong as it selects the rows with the minimum overall sequence numbers, not the minimum sequence numbers per package.
Any suggestions about the best way to do this?
One way is to use the ROW_NUMBER() function:
SELECT
EntryId
, PackageId
, SubProductID
, SubProductSequence
FROM
( SELECT
EntryId
, PackageId
, SubProductID
, SubProductSequence
, ROW_NUMBER() OVER (PARTITION BY PackageId
ORDER BY SubProductSequence ASC)
AS rna
, ROW_NUMBER() OVER (PARTITION BY PackageId
ORDER BY SubProductSequence DESC)
AS rnd
FROM
#TempPackages
) AS tmp
WHERE
rna = 1
OR rnd = 1 ;
ROW_NUMBER() is a ranking function that is used with OVER clause. What it basically does in this case, is it groups the rows with same PackageId (that is done with the PARTITION BY PackageId), then orders them by SubProductSequence (ascending or descending) and assigns a row_number, starting from 1 for each packageId.
So, the subquery would return this, if it was run alone:
EntryId (PK) PackageId SubProductID SubProductSequence rna rnd
3 1111 33 1 1 5
5 1111 122 2 2 4
1 1111 17 3 3 3
2 1111 28 4 4 2
4 1111 67 5 5 1
6 2222 18 4 1 5
7 2222 29 5 2 4
9 2222 103 7 3 3
8 2222 33 9 4 2
10 2222 99 11 5 1
15 3333 9845 1 1 5
14 3333 1023 2 2 4
13 3333 789 3 3 3
11 3333 256 5 4 2
12 3333 333 6 5 1
The WHERE condition added in the external query is obvious afterwards.
Improving on Bohemian's idea -
;WITH MinMax AS
(SELECT PackageId ,
MIN(SubProductSequence) [Min],
MAX(SubProductSequence) [Max]
FROM #TempPackages
GROUP BY PackageId )
SELECT EntryId, SubProductSequence, TP.PackageId, SubProductID FROM #TempPkges TP
INNER JOIN MinMax MM ON TP.PackageId = MM.PackageId
AND (SubProductSequence = MM.[Min] OR SubProductSequence = MM.[Max])
And then, you can add your own ORDER BY
WITH t1 AS
(SELECT PackageId,MIN(SubProductSequence) minm,MAX(SubProductSequence) maxm
FROM ##TempPackages
GROUPBY PackageId
)
SELECT pk.EntryId, pk.PackageId,pk.SubProductID, pk.SubProductSequence
FROM ##TempPackages pk INNER JOIN t1
ON pk.PackageId = t1.PackageId
WHERE pk.SubProductSequence = t1.minm OR
pk.SubProductSequence = t1.maxm