SQL Query - Looping - sql

I'm trying to output a record per part for each the quantity in the field. E.g. if a part has a qty of 10 then I'd want that part to be listed 10 times, if the qty was 2 then I'd only want the part to be list twice.
Here's a sample of the data:
Part Qty
PSR6621581 17
PSR6620952 13
PSR6620754 11
PSR6621436 11
PSR6621029 9
PSR661712 9
PSR661907 9
PSR662998 8
PSR6620574 7
PSR661781 7
Any suggestions?

You can use a recursive CTE to expand the rows. For example:
with
p as (
select part, qty, 1 as n from t
union all
select part, qty, n + 1
from p
where n < qty
)
select part, qty from p
Result:
part qty
----- ---
ABC 1
DEF 4
DEF 4
DEF 4
DEF 4
See running example at db<>fiddle.

Here is another option. This is using a tally which is the ideal way to handle this type of thing. I keep this view on my system as a crazy fast way of having a tally table.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
GO
Now we just create a dummy table with your sample data.
declare #Something table
(
Part varchar(10)
, Qty int
)
insert #Something
select 'PSR6621581', 17 union all
select 'PSR6620952', 13 union all
select 'PSR6620754', 11 union all
select 'PSR6621436', 11 union all
select 'PSR6621029', 9 union all
select 'PSR661712', 9 union all
select 'PSR661907', 9 union all
select 'PSR662998', 8 union all
select 'PSR6620574', 7 union all
select 'PSR661781', 7
Now that the setup is complete the query to produce the output you want is super easy and lightning fast to execute.
select s.Part
, s.Qty
from #Something s
join cteTally t on t.N <= s.Qty
order by s.Part
, t.N

Related

how do I count 1st column values against the second value(in which 1 st column values are repeated)

[how do I count the number of times p's value is repeated in column n ]
If I Understand what you want, you can use this code :
This Code is to create your sample data:
SELECT 1 AS N, 2 AS P INTO #t
UNION ALL
SELECT 3 AS N, 2 AS P
UNION ALL
SELECT 6 AS N, 8 AS P
UNION ALL
SELECT 9 AS N, 8 AS P
UNION ALL
SELECT 2 AS N, 5 AS P
UNION ALL
SELECT 8 AS N, 5 AS P
UNION ALL
SELECT 8 AS N, NULL AS P
Here is the code to count column
SELECT *, (SELECT COUNT(1) FROM #t t WHERE t.N = #t.p) AS c FROM #t
DROP TABLE #t

SQL Query: Count the number of distinct values in a table

I am trying to create a SQL query to count the number of distinct values in a SQL table.
My table has 6 columns:
n1 n2 n3 n4 n5 n6
______________________
3 5 7 9 11 20
3 7 11 15 17 20
3 15 26 28 30 40
15 26 30 40 55 56
3 4 5 9 15 17
17 20 26 28 30 40
And here's the result I am trying to get:
value frequency
______________________
3 4
4 1
5 2
7 2
9 2
11 2
15 3
17 3
20 3
26 3
28 2
30 3
40 3
55 1
56 1
So basically, I need the query to look at the whole table, take a note of each value that appears, and count the number of times that particular value appears.
Use UNION ALL to get all the nX column values in 1 column and aggregate:
select value, count(*) as frequency
from (
select n1 as value from tablename union all
select n2 from tablename union all
select n3 from tablename union all
select n4 from tablename union all
select n5 from tablename union all
select n6 from tablename
) t
group by value
I would recommend cross apply for this purpose:
select v.n, count(*) as frequency
from t cross apply
(values (n1), (n2), (n3), (n4), (n5), (n6)) v(n)
group by v.n;
cross apply, which implements a lateral join is more efficient than union all for unpivoting data. This is particularly true if your "table" is really a view or complex query.
here is the beautiful use case of UNPIVOT if you are using SQL SERVER or ORACLE:
SELECT
[value]
, count(*) frequency
FROM
( select n1,n2,n3,n4,n5,n6 from tablename) p
UNPIVOT ([value] for nums in ( n1,n2,n3,n4,n5,n6 )) as unpvt
GROUP BY [value]
ORDER BY frequency DESC
which is more efficient than Union , if performance matters there.

TSQL, Get top N unique rows across ordered groups

I have the following table of values, sorted by arbitrary segment id specified by the user. ( I know how to do that query and below are the results )
SegmentID SequenceID
3 100
3 200
3 400
3 430
1 100
1 200
1 300
1 410
2 100
2 200
2 300
2 420
I need a SQL query ( Sql Server 2012 ) that returns top N Records in order of Precedence where SequenceID is not repeated.
Example: user wants 7 sequences in order of segment preference: 3, 1,2.
The correct answer is
SegmentID SequenceID
3 100
3 200
3 400
3 430
1 300
1 410
2 420
in a nutshell, i need to traverse recordset from top to bottom, grab unique sequences as i go and add to the list.
How can I do that in a TSql statement?
create table #data (SegmentID int,SequenceID int);
insert into #data values
(3,100),
(3,200),
(3,400),
(3,430),
(1,100),
(1,200),
(1,300),
(1,410),
(2,100),
(2,200),
(2,300),
(2,420);
This table declares the ordering preference:
create table #prefs (Preference int, SegmentID int);
insert into #prefs values(1,3),(2,1),(3,2);
with cte as
(
select #data.SegmentID,
#data.SequenceID,
Preference,
row_number() over (partition by SequenceID order by Preference) rn
from #data
inner join #prefs on #data.SegmentID = #prefs.SegmentID
)
select SegmentId,
SequenceID
from cte
where rn = 1
order by Preference, SequenceID;
DEMO:
http://rextester.com/JKNKD15000
With cte (SegmentID, SequenceID) as
(SELECT 3, 100 UNION ALL
SELECT 3, 200 UNION ALL
SELECT 3, 400 UNION ALL
SELECT 3, 430 UNION ALL
SELECT 1, 100 UNION ALL
SELECT 1, 200 UNION ALL
SELECT 1, 300 UNION ALL
SELECT 1, 410 UNION ALL
SELECT 2, 100 UNION ALL
SELECT 2, 200 UNION ALL
SELECT 2, 300 UNION ALL
SELECT 2, 420),
userOrder (SegmentID, orderID) as (
SELECT 3, 1 UNION ALL
SELECT 1, 2 UNION ALL
SELECT 2, 3),
Results (SegmentID, SequenceID, RN, orderID) as (
Select A.*
, Row_number() over (Partition by A.SequenceID order by B.orderID) RN
, B.orderID
from cte A
INNER JOIN userOrder B
on A.SegmentID = B.SegmentID)
Select Top 7 *
from results where RN = 1
order by OrderID, SequenceID

How to display duplicate items based on the quantity in SQL?

I have a table with the field content :
item qty
----- -----
tea 2
I want to display as below
item qty
---- ------
tea 1
tea 1
How to create SQL query like above ?
SAMPLE TABLE
CREATE TABLE #TEMP(ITEM VARCHAR(10),QTY INT)
INSERT INTO #TEMP
SELECT 'TEA',2
UNION ALL
SELECT 'COFFEE',3
If you have more than 1 type of item and you want to get the number of items, you can do with the following query.
QUERY
;WITH CTE AS
(
-- You will get each ITEM from your table
SELECT ITEM, QTY, 1 NEWQTY
FROM #TEMP
UNION ALL
-- Here it will loop and shows the repeated values of each ITEM
SELECT C1.ITEM,C1.QTY,NEWQTY + 1
FROM CTE C1
JOIN #TEMP T1 ON C1.item= T1.ITEM
WHERE C1.NEWQTY < T1.qty
)
SELECT ITEM,
1 QTY
FROM CTE
ORDER BY ITEM
Click here to view result
Use Recursive CTE
;WITH cte
AS (SELECT item,qty,1 num from yourtable
UNION ALL
SELECT item,
qty,
num + 1
FROM cte
WHERE num < qty)
SELECT item,
1 as Qty
FROM cte
or use Tally table. It will have a better performance when compared to Recursive CTE
;WITH e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 10
e2(n)
AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b) -- 10*10
--e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100
SELECT a.item,
1 AS qty
FROM yourtable a
JOIN (SELECT n = Row_number()OVER (ORDER BY n)
FROM e2) b
ON b.n <= a.qty;
Note : Based on the Quantity you may have to increase the cross join of CTE's
Check here for more info

How can I find unoccupied id numbers in a table?

In my table I want to see a list of unoccupied id numbers in a certain range.
For example there are 10 records in my table with id's: "2,3,4,5,10,12,16,18,21,22" and say that I want to see available ones between 1 and 25. So I want to see a list like:
1,6,7,89,11,13,14,15,17,19,20,23,24,25
How should I write my sql query?
Select the numbers form 1 to 25 and show only those that are not in your table
select n from
( select rownum n from dual connect by level <= 25)
where n not in (select id from table);
Let's say you a #numbers table with three numbers -
CREATE TABLE #numbers (num INT)
INSERT INTO #numbers (num)
SELECT 1
UNION
SELECT 3
UNION
SELECT 6
Now, you can use CTE to generate numbers recursively from 1-25 and deselect those which are in your #numbers table in the WHERE clause -
;WITH n(n) AS
(
SELECT 1
UNION ALL
SELECT n+1 FROM n WHERE n < 25
)
SELECT n FROM n
WHERE n NOT IN (select num from #numbers)
ORDER BY n
OPTION (MAXRECURSION 25);
You can try using the "NOT IN" clause:
select
u1.user_id + 1 as start
from users as u1
left outer join users as u2 on u1.user_id + 1 = u2.id
where
u2.id is null
see also SQL query to find Missing sequence numbers
You need LISTAGG to get the output in a single row.
SQL> WITH DATA1 AS(
2 SELECT LEVEL rn FROM dual CONNECT BY LEVEL <=25
3 ),
4 data2 AS(
5 SELECT 2 num FROM dual UNION ALL
6 SELECT 3 FROM dual UNION ALL
7 SELECT 4 from dual union all
8 SELECT 5 FROM dual UNION ALL
9 SELECT 10 FROM dual UNION ALL
10 SELECT 12 from dual union all
11 SELECT 16 from dual union all
12 SELECT 18 FROM dual UNION ALL
13 SELECT 21 FROM dual UNION ALL
14 SELECT 22 FROM dual)
15 SELECT listagg(rn, ',')
16 WITHIN GROUP (ORDER BY rn) num_list FROM data1
17 WHERE rn NOT IN(SELECT num FROM data2)
18 /
NUM_LIST
----------------------------------------------------
1,6,7,8,9,11,13,14,15,17,19,20,23,24,25
SQL>