Reverse/Blow out a GROUP BY - sql

I am working with data that is grouped by item number with counts. Each record with a count > 2 needs to be blown out into individual records and compared at that level to a different set of data.
The data looks like this (It is stuck in this format. This is the only way the customer can send it.):
OwnerNumber ItemCode ItemNumber CountOfItems
1234 Item1 Item1-001 3
1234 Item1 Item1-002 1
1234 Item1 Item1-003 2
1234 Item2 Item2-001 1
And I need the data formatted like this (dynamically without hardcoding for value of CountOfItems):
OwnerNumber ItemCode ItemNumber
1234 Item1 Item1-001
1234 Item1 Item1-001
1234 Item1 Item1-001
1234 Item1 Item1-002
1234 Item1 Item1-003
1234 Item1 Item1-003
1234 Item2 Item2-001
For some reason I just can't wrap my head around a clean way to do this (or any way).

You can manage with a Common Table Expression
WITH CTE AS (
SELECT OwnerNumber,ItemCode,ItemNumber,CountOfItems FROM table
UNION ALL SELECT OwnerNumber,ItemCode,ItemNumber,CountOfItems-1
FROM CTE
WHERE CountOfItems >= 2
)
SELECT OwnerNumber,ItemCode,ItemNumber
FROM CTE
ORDER BY ItemNumber
OPTION (MAXRECURSION 0);
Edit:
Added MAXRECURSION to handle situations where CountOfItems exceeds default max recursions as pointed out by Dev_etter

Hmmm.... I think I like recursive CTEs for this:
WITH Data (OwnerNumber, ItemCode, ItemNumber, CountOfItems) as (
SELECT OwnerNumber, ItemCode, ItemNumber, CountOfItems
FROM OriginalTable
UNION ALL
SELECT OwnerNumber, ItemCode, ItemNumber, CountOfItems - 1
FROM Data
WHERE CountOfItems > 1)
SELECT OwnerNumber, ItemCode, ItemNumber
FROM Data
ORDER BY OwnerNumber, ItemCode, ItemNumber

You can avoid recursion with the query below, and I think will be more efficient. Here, the table N is any table with at least as many rows as the largest CountOfItems value.
This is a rare example of a query where TOP without ORDER BY is not a bad idea.
select
OwnerNumber,
ItemCode,
ItemNumber
from t
cross apply (
select top (CountOfItems) null
from N
) as N(c)

Related

Query to get output for stock

I need output through query as per given below points
there is stock table contains all items with multiple salemrp
I need those records whose stock is present in multiple salemrp, but if there is only one salemrp for an item, I require also those records.
My query is as follows, please help me, how to manipulate this query.
SELECT
itemid,
grpid,
(SELECT
itemname
FROM sap_itemmASter
WHERE itemid=sap_stockmASter.itemid )AS itemname,
(SELECT grpname
FROM sap_grpmASter
WHERE grpid=sap_stockmASter.grpid) AS grpname,
(SELECT partno
FROM sap_itemmASter
WHERE itemid=sap_stockmASter.itemid) AS partno,
salemrp,
(SELECT brANDname
FROM sap_brANDmASter
WHERE brANDid IN (SELECT brANDid
FROM sap_itemmASter
WHERE itemid=sap_stockmASter.itemid)) AS catname,
(laneno + ' - ' + rackno) AS locno,
isnull(SUM(stkqty),0) AS balqty,
(SELECT top 1 (laneno + ' - ' + rackno)
FROM sap_stockloc
WHERE itemid=sap_stockmASter.itemid) AS storeloc
FROM sap_stockmASter
WHERE laneno!=''
AND itemid>0
AND grpid IN (SELECT grpid
FROM sap_grpmASter
WHERE isactive=1
AND isdel=1)
AND itemid IN (SELECT itemid
FROM sap_itemmASter
WHERE isdel=1
AND isactive=1
AND laneno=sap_stockmASter.laneno
AND rackno=sap_stockmASter.rackno)
AND grpid=37
GROUP BY itemid,grpid,laneno,rackno,salemrp
ORDER BY itemname
More information:
Item Name SaleMrp Qty
ABC 158.00 48
ABC 165.00 -11
ABC 170.00 5
In this I want to not display negative stock, but
XYZ 125.00 0
(I need this record as well, beacause it has only one mrp)
PQR 100.00 -5
(I need this record as well, beacause it has only one mrp)

Split a row into multiple rows based on a column value SQL

I have the following "Order Table" :
Item Quantity
pencil 2
pen 1
Notebook 4
I need the result like :
Item Quantity
pencil 1
pencil 1
pen 1
Notebook 1
Notebook 1
Notebook 1
Notebook 1
You didn't specify which RDBMS you are using, so how you generate the numbers will depend on that (maybe a recursive CTE for SQL Server, using DUAL for Oracle, etc.). I've only written code to handle the data that you've shown, but you'll obviously need to account for numbers larger than four in the final solution.
SELECT
MT.sr_no,
MT.item_name,
1 AS quantity
FROM
My_Table MT
INNER JOIN
(
SELECT 1 AS nbr UNION ALL SELECT 2 AS nbr UNION ALL
SELECT 3 AS nbr UNION ALL SELECT 4 AS nbr
) N ON N.nbr <= MT.quantity
You can use the recursive query using common table expression to generate number duplicate rows according to the quantity field as below
WITH cte (sno,item,quantity,rnum)
AS
(
SELECT sno,item,quantity, 1 as rnum
FROM [Order]
UNION ALL
SELECT cte.sno,cte.item,cte.quantity, rnum+1
FROM [Order] JOIN cte ON [Order].sno = cte.sno
AND cte.rnum < [Order].quantity
)
SELECT item,1 AS Quantity
FROM cte
ORDER BY sno

Update SQL Query to add rows from a single table but combine 1 columns values togethor

I am trying to create a query that will select a list of items that have the same information in all the columns except 1, a Total # of Units. I need the query to combine those rows.
Example Columns
In the table I have 3 rows that look like this:
Date item # Description qty
---------------------------------
20150910 1233 lettuce 1.20
20150910 1234 cheese 3.40
20150910 1234 cheese 2.65
20150910 1234 cheese 1.00
I want to run an update query to update the table to look like this:
20150910, 1233, lettuce, 1.20
20150910, 1234, cheese, 7.05
Your help is appreciated!
It is just a grouped summation, except that you want to do the deletion. Instead of deleting the old rows, it is actually easier to just insert the data into a new table with the same schema, or rename the existing table to Table_Old.
You can then select the summary data from the old table with:
SELECT Code, Id, Description, SUM(Amount)
FROM Table
GROUP BY Code, Id, Description
This way you also have the old data as a backup, in case you mess it up! :)
You just need to sum qty
SELECT Date, Item, Description, SUM(qty)
FROM your_table
GROUP BY Date, Item, Description
Edit: For updating the table, assuming you have no negative values in qty, I'd do
UPDATE your_table yt
SET yt.qty = (SELECT SUM(yt2.qty)
FROM your_table yt2
WHERE yt.date = yt2.date and yt.Item=yt2.Item AND yt.Description = yt2.Description);
DELETE FROM your_table yt
WHERE qty <> (SELECT MAX(yt2.qty)
FROM your_table yt2
WHERE yt.date = yt2.date and yt.Item=yt2.Item AND yt.Description = yt2.Description);
This should do the trick, first you sum it and then delete every row that has qty different from the one you have just sumed.
Note that you don't need to group by in the subqueries as you're filtering the items you are updating/deleting

SQL query Splitting a column into Multiple rows divide by percentage

How to get percentage of a column and then inserting it as rows
Col1 item TotalAmount**
1 ABC 5558767.82
2 ABC 4747605.5
3 ABC 667377.69
4 ABC 3844204
6 CTB 100
7 CTB 500.52
I need to create a new column percentage for each item which is I have done as :-
Select item, (totalAmount/select sum(totalAmount) from table1) as Percentage
From table1
Group by item
Col1 item TotalAmount percentage
1 ABC 5558767.82 38
2 ABC 4747605.5 32
3 ABC 667377.69 5
4 ABC 3844204 26
6 CTB 100 17
7 CTB 500.52 83
Now, the complex part I have to calculate another amount by multiplying this percentage to an amount from another table say table2
ii) update the Total amount column by spilt the total amount column of table 1 into 2 rows – 1st row of the new Calculate PledgeAmount and 2nd row – (totalAmount – PledgeAmount)
*Select t1.percentage * t2.new amount as [PledgeAmount]
From table 1 join table2 where t1.item=t2.item*
. e.g. for col1 Amount of 5558767.82 will split into two rows.
Final Result sample for :-
Col1 item TotalAmount Type
1 ABC 363700.00 Pledge
1 ABC 5195067.82 Unpledge
....
I am using Temporary table to do calculations.
One of the way I think is to calculate the Pledged and Unpledged amount as new column and Pivot it but its huge table with hundreds of columns it will not perform fast.
Any other efficient way?
You can use a windowing function to solve this problem -- first in a sub-query calculate the total and then in the main query the percent:
Select *, (totalAmount/total_for_item)*100 as percent_of_total
from (
SELECT t.*,
SUM(totalAmount) OVER (PARTITION BY item) as total_for_item
FROM table t
) sub
First, let's get the total amount per item:
SELECT item, SUM( totalAmount ) as sumTotal
INTO #totalperitem
FROM table1
GROUP BY item
Now it's easy to get to the percentages:
SELECT t1.Col1,
t1.item,
t1.totalAmount,
t1.totalAmount/tpi.sumTotal*100 AS percentage
FROM table1 t1
INNER JOIN #totalperitem tpi on ...
Tricky part: Separate rows with/without match in table2. Can be done with a WHERE NOT EXISTS, or, my preference, with a single outer join:
SELECT t1.item,
CASE WHEN tpledged.item IS NULL
THEN "Unpledged"
ELSE "Pledged"
END,
SUM( t1.totalAmount ) AS amount
FROM table1 t1
LEFT OUTER JOIN table2 tpledged ON t1. ... = tpledged. ...
GROUP BY t1.item,
CASE WHEN tpledged.item IS NULL
THEN "Unpledged"
ELSE "Pledged"
END
The basic trick is to create an artificial column from the presence/absence of records in table2 and to also group by that artificial column.

SQL return multiple rows from one record

This is the opposite of reducing repeating records.
SQL query to create physical inventory checklists
If widget-xyz has a qty of 1 item return 1 row, but if it has 5, return 5 rows etc.
For all widgets in a particular warehouse.
Previously this was handled with a macro working through a range in excel, checking the qty column. Is there a way to make a single query instead?
The tables are FoxPro dbf files generated by an application and I am outputting this into html
Instead of generating an xml string and using xml parsing functions to generate a counter as Nestor has suggested, you might consider joining on a recursive CTE as a counter, as LukLed has hinted to:
WITH Counter AS
(
SELECT 0 i
UNION ALL
SELECT i + 1
FROM Counter
WHERE i < 100
),
Data AS
(
SELECT 'A' sku, 1 qty
UNION
SELECT 'B', 2
UNION
SELECT 'C', 3
)
SELECT *
FROM Data
INNER JOIN Counter ON i < qty
According to query analyzer, this query is much faster than the xml pseudo-table. This approach also gives you a recordset with a natural key (sku, i).
There is a default recursion limit of 100 in MSSQL that will restrict your counter. If you have quantities > 100, you can either increase this limit, use nested counters, or create a physical table for counting.
For SQL 2005/2008, take a look at
CROSS APPLY
What I would do is CROSS APPLY each row with a sub table with as many rows as qty has. A secondary question is how to create that sub table (I'd suggest to create an xml string and then parse it with the xml operators)
I hope this gives you a starting pointer....
Starting with
declare #table table (sku int, qty int);
insert into #table values (1, 5), (2,4), (3,2);
select * from #table;
sku qty
----------- -----------
1 5
2 4
3 2
You can generate:
with MainT as (
select *, convert(xml,'<table>'+REPLICATE('<r></r>',qty)+'</table>') as pseudo_table
from #table
)
select p.sku, p.qty
from MainT p
CROSS APPLY
(
select p.sku from p.pseudo_table.nodes('/table/r') T(row)
) crossT
sku qty
----------- -----------
1 5
1 5
1 5
1 5
1 5
2 4
2 4
2 4
2 4
3 2
3 2
Is that what you want?
Seriously dude... next time put more effort writing your question. It's impossible to know exactly what you are looking for.
You can use table with number from 1 to max(quantity) and join your table by quantity <= number. You can do it in many ways, but it depends on sql engine.
You can do this using dynamic sql.