explode tables in sql - sql

Problem statement:
I have Product_id and Quantity column in my table
Say product_id A has 2 units, B has 3 units etc
How do i make a table with 2 rows of Product_id A, 3 rows of Product_id B?

I did this exercise based on a recursive Common Table Expression:
;WITH CTE (Vals)
AS (
SELECT 1
UNION ALL
SELECT 1 + Vals
FROM CTE WHERE Vals< 99
)
SELECT Product_id
FROM Mytable A
INNER JOIN CTE C ON C.Vals <= A.Quantity
ORDER BY A.Product_id
supposing Quantity < 99
could help you

This worked for me, tested on your example :
WITH tally(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM tally WHERE n<100)
SELECT Product_id, 1 as Quantity
FROM MyTable CROSS JOIN tally
WHERE n<=quantity
ORDER BY Product_id;

Related

Repeating rows based on count in a different column - SQL

I have a table that holds IDs and count. I want to repeat the rows the number of times mentioned in the count.
My table:
Desired output:
My code:
create table #temp1(CID int, CVID int, count int)
insert #temp1
values
(9906, 4687, 4),
(9906, 4693, 5)
create table #temp2 (CID int,CVID int, count int,ro int)
;with t3 as (
select c.CID,c.CVID, c.count, row_number() over (partition by c.CID order by c.CID) ro
from #temp1 c
)
insert #temp2
select CID,CVID,count,ro from t3 where ro <= count
My code is missing something that its not producing desired result. Any help?!
You need a numbers table up to the maximum value of count column which can then be used to generate multiple rows. This number generation can be done using a recursive cte.
--Recursive CTE
with nums(n) as (select max(count) from #temp1
union all
select n-1
from nums
where n > 1
)
--Query to generate multiple rows
select t.*,nums.n as ro
from #temp1 t
join nums on nums.n <= t.count
Just another option is an ad-hoc tally table
Example
Select A.*
,Ro = B.N
From YourTable A
Join ( Select Top 1000 N=Row_Number() Over (Order By (Select NULL))
From master..spt_values n1 ) B on B.N<=A.[Count]
Returns
CID CVID COUNT Ro
9906 4687 4 1
9906 4687 4 2
9906 4687 4 3
9906 4687 4 4
9906 4693 5 1
9906 4693 5 2
9906 4693 5 3
9906 4693 5 4
9906 4693 5 5
I would use a recursive CTE, but directly:
with cte as (
select CID, CVID, count, 1 as ro
from #temp1
union all
select CID, CVID, count, ro + 1
from cte
where cte.ro < cte.count
)
select cte.*
from cte;
If your counts exceed 100, then you'll need to use option (maxrecursion 0).
Thanks all for all the suggestion. I used the below query to solve my problem:
;with cte(cid, cvid,count, i) as
(
select cid
, cvid
, count
, 1
from #temp1
union all
select cid
, cvid
, count
, i + 1
from cte
where cte.i < cte.count
)
select *
from cte
order by
cid,count

How to exclude some (not all) record that has same values

After query table A using first query, I have these records:
pID cID code
1 1 A
1 1 B
1 1 B
1 1 B
After query table B using second query, I have one record:
pID cID code
1 1 B
1 1 B
I want table A exclude the records of table B. The result is:
pID cID code
1 1 A
1 1 A
How can I do that? Hope u could help me. thanks.
Updating...
Sorry for the example to make you confuse
If I got these record from second table:
pID cID code
1 1 B
Then the result I want is (exclude one record):
pID cID code
1 1 A
1 1 B
1 1 B
you try GROUP BY function in your Query
example :
select pID,cID,code from table group by code
using EXCEPT and row_number() to generate a unique no
;with cte1 as
(
select *, rn = row_number() over (partition by pID, cID, code order by pID, cID, code)
from query1
),
cte2 as
(
select *, rn = row_number() over (partition by pID, cID, code order by pID, cID, code)
from query2
)
select *
from cte1
except
select *
from cte2
Based on your question, which I think you want to delete the records from B which occur more than once in A:
first select all records from A which are not there in B and then union them 1 distinct records which are there in both A and B:
select * from A
except
select * from B
union all
select distinct *
from
(select a.pid, a.cid, a.code
from
A
inner join
B
on a.pid=b.pid and a.cid=b.cid and a.code=b.code)
Just use EXCEPT. How ever your desired output is wrong as 1 1 B also the same record from TableB
SELECT * FROM TABLE_A
EXCEPT
SELECT * FROM TABLE_B
Refer this Link
If your case NOt all But some then.
Simply you can use DISTINCT
As per the UPdate in Question (From what I understood)
SELECT DISTINCT * FROM TABLE_A
UNION ALL
SELECT * FROM TABLE_B

SUM Column in SQL

I have a table in SQL Server, and I need to sum a column, like the example below:
CREATE TABLE B
(
ID int,
Qty int,
)
INSERT INTO B VALUES (1,2)
INSERT INTO B VALUES (2,7)
INSERT INTO B VALUES (3,2)
INSERT INTO B VALUES (4,11)
SELECT *, '' AS TotalQty FROM B
ORDER BY ID
In this example what I need is the column TotalQty give me the values like:
2
9
11
22
How can it be achieved?
You can use SUM in a co-related subquery or CROSS APPLY like this
Co-related Subquery
SELECT ID,(SELECT SUM(Qty) FROM B WHERE B.id <= C.id) FROM B as C
ORDER BY ID
Using CROSS APPLY
SELECT ID,D.Qty FROM B as C
CROSS APPLY
(
SELECT SUM(Qty) Qty
FROM B WHERE B.id <= C.id
)AS D
ORDER BY ID
Output
1 2
2 9
3 11
4 22
If you were using SQL Server 2012 or above, SUM() with Over() clause could have been used like this.
SELECT ID, SUM(Qty) OVER(ORDER BY ID ASC) FROM B as C
ORDER BY ID
Edit
Another way to do this in SQL Server 2008 is using Recursive CTE. Something like this.
Note: This method is based on the answer by Roman Pekar on this thread Calculate a Running Total in SQL Server. Based on his observation this would perform better than co related subquery and CROSS APPLY both
;WITH CTE as
(
SELECT ID,Qty,ROW_NUMBER()OVER(ORDER BY ID ASC) as rn
FROM B
), CTE_Running_Total as
(
SELECT Id,rn,Qty,Qty as Running_Total
FROM CTE
WHERE rn = 1
UNION ALL
SELECT C1.Id,C1.rn,C1.Qty,C1.Qty + C2.Running_Total as Running_Total
FROM CTE C1
INNER JOIN CTE_Running_Total C2
ON C1.rn = C2.rn + 1
)
SELECT *
FROM CTE_Running_Total
ORDER BY Id
OPTION (maxrecursion 0)

Split a row on 2 or more rows depending on a column

I have a question
If I have one row that looks like this
|ordernumber|qty|articlenumber|
| 123125213| 3 |fffff111 |
How can I split this into three rows like this:
|ordernumber|qty|articlenumber|
| 123125213| 1 |fffff111 |
| 123125213| 1 |fffff111 |
| 123125213| 1 |fffff111 |
/J
You can use recursive CTE:
WITH RCTE AS
(
SELECT
ordernumber, qty, articlenumber, qty AS L
FROM Table1
UNION ALL
SELECT
ordernumber, 1, articlenumber, L - 1 AS L
FROM RCTE
WHERE L>0
)
SELECT ordernumber,qty, articlenumber
FROM RCTE WHERE qty = 1
SQLFiddleDEMO
EDIT:
Based on Marek Grzenkowicz's answer and MatBailie's comment, whole new idea:
WITH CTE_Nums AS
(
SELECT MAX(qty) n FROM dbo.Table1
UNION ALL
SELECT n-1 FROM CTE_Nums
WHERE n>1
)
SELECT ordernumber ,
1 AS qty,
articlenumber
FROM dbo.Table1 t1
INNER JOIN CTE_Nums n ON t1.qty >= n.n
Generating number from 1 to max(qty) and join table on it.
SQLFiddle DEMO
Here's a quick hack using an additional table populated with a number of rows suitable for the qty values you are expecting:
-- helper table
CREATE TABLE qty_splitter (qty int)
INSERT INTO qty_splitter VALUES (1)
INSERT INTO qty_splitter VALUES (2)
INSERT INTO qty_splitter VALUES (3)
INSERT INTO qty_splitter VALUES (4)
INSERT INTO qty_splitter VALUES (5)
....
-- query to produce split rows
SELECT t1.ordernumber, 1, t1.articlenumber
FROM table1 t1
INNER JOIN qty_splitter qs on t.qty >= qs.qty
You can do it using CTE
declare #t table (ordername varchar(50), qty int)
insert into #t values ('ord1',5),('ord2',3)
;with cte as
(
select ordername, qty, qty-1 n
from #t
union all
select ordername, qty, n-1
from cte
where n>0
)
select ordername,1
from cte
order by ordername
Also you can use option with master..spt_values system table.
SELECT t.ordernumber, o.qty, t.articlenumber
FROM dbo.SplitTable t CROSS APPLY (
SELECT 1 AS qty
FROM master..spt_values v
WHERE v.TYPE = 'P' AND v.number < t.qty
) o
However, for this purpose is preferable to use its own sequence table
See demo on SQLFiddle

SQL Query, Union Order by like a tree

Hello I have a query like
SELECT 4 AS sortf,XX FROM Table GROUP BY Y
UNION
SELECT 1 AS sortf,XX FROM Table GROUP BY Y
UNION
SELECT 2 AS sortf,XX FROM Table GROUP BY Y
UNION
SELECT 3 AS sortf,XX FROM Table GROUP BY Y
ORDER BY 3,2
My problem is that the line 2 and 3 ar not ordered like a tree. I tried some other combinations but it did not work.
if you want to sort your dataset according to numbers put your code and unions into a common table expression and the use ROW_NUMBER() function to generates row number , something like this :
WITH CTE
AS
(
SELECT 4 AS sortf, productid FROM Production.Products
UNION
SELECT 1 AS sortf,productid FROM Production.Products
UNION
SELECT 2 AS sortf,productid FROM Production.Products
UNION
SELECT 3 AS sortf,productid FROM Production.Products
)
SELECT *, ROW_NUMBER() OVER (ORDER BY productid) AS SortOrder
FROM CTE
ORDER BY SortOrder