SQL return multiple rows from one record - sql

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.

Related

Snowflake: Repeating rows based on column value

How to repeat rows based on column value in snowflake using sql.
I tried a few methods but not working such as dual and connect by.
I have two columns: Id and Quantity.
For each ID, there are different values of Quantity.
So if you have a count, you can use a generator:
with ten_rows as (
select row_number() over (order by null) as rn
from table(generator(ROWCOUNT=>10))
), data(id, count) as (
select * from values
(1,2),
(2,4)
)
SELECT
d.*
,r.rn
from data as d
join ten_rows as r
on d.count >= r.rn
order by 1,3;
ID
COUNT
RN
1
2
1
1
2
2
2
4
1
2
4
2
2
4
3
2
4
4
Ok let's start by generating some data. We will create 10 rows, with a QTY. The QTY will be randomly chosen as 1 or 2.
Next we want to duplicate the rows with a QTY of 2 and leave the QTY =1 as they are.
Obviously you can change all parameters above to suit your needs - this solution works super fast and in my opinion way better than table generation.
Simply stack SPLIT_TO_TABLE(), REPEAT() with a LATERAL() join and voila.
WITH TEN_ROWS AS (SELECT ROW_NUMBER()OVER(ORDER BY NULL)SOME_ID,UNIFORM(1,2,RANDOM())QTY FROM TABLE(GENERATOR(ROWCOUNT=>10)))
SELECT
TEN_ROWS.*
FROM
TEN_ROWS,LATERAL SPLIT_TO_TABLE(REPEAT('hire me $10/hour',QTY-1),'hire me $10/hour')ALTERNATIVE_APPROACH;

SQL Server - Manipulate data and return new results

Is there any way to do something in SQL Server to manipulate data like manipulating arrays in any other programming language?
I have one SQL query that returns 3 columns, "dt_ref" (date), "vlr_venda" (float) and "qt_parcelas" (int)
Basically, I need to do something like this:
- When field "qt_parcelas" is higher than 1, I need to do a "loop" with this row and generate 3 rows.
So, I need to divide the field "vlr_venda" by field "qt_parcelas", and use the field "dt_ref" as reference for the date start and increment month in the date field for the value of "qt_parcelas"
For example, if my query returns these structure:
| dt_ref | vlr_venda | qt_parcelas |
-------------------------------------
|20180901 | 3000 | 3 |
I need to do something to return this:
| dt_ref | vlr_venda |
----------------------
|20180901 | 1000 |
|20181001 | 1000 |
|20181101 | 1000 |
Is it possible to do it in SQL Server?
I've searched for something like this but haven't found anything useful...
Any ideas?
You can use a recursive CTE: Sql Fiddle
with cte as (
select dt_ref, vlr_venda / qt_parcelas as new_val, qt_parcelas, 1 as num
from t
union all
select dateadd(month, 1, dt_ref), new_val, qt_parcelas, num + 1
from cte
where num < qt_parcelas
)
select dt_ref, new_val
from cte;
As written, this will work for up to 100 months. You need to add OPTION (MAXRECURSION 0) for longer periods.
Instead of using a rCTE you could use a tally table. If you have much larger numbers than 3 this'll probably be far be efficient:
WITH N AS(
SELECT n
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) N(n)),
Tally AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) I
FROM N N1 --10 rows
CROSS JOIN N N2 --100 rows
--CROSS JOIN N N3 --Keep adding more CROSS JOINs to create more rows
),
VTE AS (
SELECT CONVERT(date,V.dt_ref) AS dt_ref,
V.vlr_venda,
V.qt_parcelas
FROM (VALUES('20180901',3000,3),
('20181001',12000,6)) V(dt_ref,vlr_venda ,qt_parcelas))
SELECT DATEADD(MONTH,T.I,V.dt_ref),
CONVERT(decimal(10,4),V.vlr_venda / (V.qt_parcelas * 1.0)) --incase you need decimal points
FROM VTE V
JOIN Tally T ON V.qt_parcelas >= T.I;
I developed a software to generate tickets and i had a similar experience than you. I have tried CURSORS and recursive CTE and they all took something like 50 minutes when creating tickets for clients
I used this function to replicate my clients and generate my tickets
/****** Object: UserDefinedFunction [dbo].[NumbersTable] Script Date: 28/09/2018 10:51:25 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[NumbersTable] (
#fromNumber int,
#toNumber int,
#byStep int
)
RETURNS #NumbersTable TABLE (i int)
AS
BEGIN
WITH CTE_NumbersTable AS (
SELECT #fromNumber AS i
UNION ALL
SELECT i + #byStep
FROM CTE_NumbersTable
WHERE
(i + #byStep) <= #toNumber
)
INSERT INTO #NumbersTable
SELECT i FROM CTE_NumbersTable OPTION (MAXRECURSION 0)
RETURN;
END
GO
Then you can use a
CROSS APPLY dbo.NumbersTable(1,qt_parcelas ,1);
To generate your rows
Believe me this way is more efficient and when dealing with large amount of data (something like 8 to 10 million rows) it takes something like 2 minutes instead of 40

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

Returning several rows from a single query, based on a value of a column

Let's say I have this table:
|Fld | Number|
1 5
2 2
And I want to make a select that retrieves as many Fld as the Number field has:
|Fld |
1
1
1
1
1
2
2
How can I achieve this? I was thinking about making a temporary table and instert data based on the Number, but I was wondering if this could be done with a single Select statement.
PS: I'm new to SQL
You can join with a numbers table:
SELECT Fld
FROM yourtable
JOIN Numbers
ON yourtable.Number <= Numbers.Number
A numbers table is just a table with a list of numbers:
Number
1
2
3
etc...
Not an great solution (since you still query your table twice, but maybe you can work from it)
SELECT t1.fld, t1.number
FROM table t1, (
SELECT ROWNUM number FROM dual
CONNECT BY LEVEL <= (SELECT MAX(number) FROM t1)) t2
WHERE t2.number<=t1.number
It generates maximum amount of rows needed and then filters it by each row.
I don't know if your RDBMS version supports it (although I rather suspect it does), but here is a recursive version:
WITH remaining (fld, times) as (SELECT fld, 1
FROM <table>
UNION ALL
SELECT a.fld, a.times + 1
FROM remaining as a
JOIN <table> as b
ON b.fld = a.fld
AND b.number > a.times)
SELECT fld
FROM remaining
ORDER BY fld
Given your source data table, it outputs this (count included for verification):
fld times
=============
1 1
1 2
1 3
1 4
1 5
2 1
2 2

Repeating Record Sequence using SQL

This could easily be done using code, but I wondered if it could be done at the database level using SQL Server (2008).
I have a table similar to below:
CROP_ID YEAR_ PRODUCTION
1 1 0
1 2 300
1 3 500
2 1 100
2 2 700
I want to be able to run a query to repeat this for n number of years, per crop type e.g.
CROP_ID YEAR_ PRODUCTION
1 1 0
1 2 300
1 3 500
1 4 0
1 5 300
1 6 500
etc.
I'm not sure of the best approach, I presume I would need a SP and pass in a year variable, and use a loop construct? However the exact syntax escapes me. Any help appreciated.
Update
Sorry for not providing all the information in my original post. The table will allow for multiple crop types, and for Produciton values to be updated so Case statements with fixed variables are not really suitable. Apologies for not being clearer.
Update
With the TVF answer I used the following modified SQL to select by CropType for 20 years.
select top 20 b.CROP_ID,
YEAR_ = n.num * (select count() from MyBaseTable where CROP_ID = 3) + b.YEAR,
b.PRODUCTION from MyBaseTable b, dbo.fnMakeNRows(20) n
where CROP_ID = 3
You can do this in standard SQL without creating a stored procedure or using temp tables. The example below will do this for 12 years. You can extend it out to any number of years:
insert into CropYield
(CropID, Year_, Production)
Select 1, a.a + (10 * b.a),
case (a.a + (10 * b.a)) % 3
when 0 then 500
when 1 then 0
when 2 then 300
end
from (Select 0 as a union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) as a
cross join (Select 0 as a union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) as b
where a.a + (10 * b.a) between 1 and 12
You could use a table-valued-function instead of a stored proc, which gives a little more flexibility for what you do with the result (as it can be selected from directly, inserted into another table, joined to other tables, etc).
You could also make this more generic by having a TVF generate N rows (with a number from 0 to N-1 on each row) and then use some simple expressions to generate the columns you need from this. I have found such a TVF to be useful in a variety of situations.
If you need to generate more complicated data than you can with 0..N-1 and simple expressions, then you should create a TVF dedicated to your specific needs.
The following example shows how a generic TVF could be used to generate the data you ask for:
create function fnMakeNRows (#num as integer)
returns #result table (num integer not null) as
begin
if #num is null or #num = 0
begin
return
end
declare #n as integer
set #n = 0
while #n < #num
begin
insert into #result values (#n)
set #n = #n + 1
end
return
end
go
select
CROP_ID = 1,
YEAR_ = num,
PRODUCTION = case num % 3 when 0 then 0 when 1 then 300 else 500 end
from dbo.fnMakeNRows(100000)
You can also use this to duplicate rows in an existing table (which I think is more like what you want). For example, assuming base_table contains the three rows at the beginning of your question, you can turn the 3 rows into 60 rows using the following:
select
b.CROP_ID,
YEAR_ = n.num * (select count(*) from base_table) + b.YEAR_,
b.PRODUCTION
from base_table b, dbo.fnMakeNRows(20) n
This (hopefully) shows the utility of a generic fnMakeNRows function.
A common trick to produce this kind of data without the need of a stored procedure is with the use of a table of constants. Because such a table can be of generic use, it can be created with say all the integers between 1 and 100 or even 1 and 1,000 depending on usage.
For exmaple
CREATE TABLE tblConstNums
( I INT )
INSERT INTO tblConstNums VALUES (1)
INSERT INTO tblConstNums VALUES (2)
INSERT INTO tblConstNums VALUES (3)
INSERT INTO tblConstNums VALUES (4)
INSERT INTO tblConstNums VALUES (5)
-- ...
INSERT INTO tblConstNums VALUES (1000)
The the solution can be written declaratively (without requiring Stored Procedure or more generally procedural statements:
SELECT CROP_ID, YEAR_ * I, PRODUCTION
FROM myCropTable T
JOIN tblConstNums C on 1=1
WHERE I in (1, 2, 3)
order by CROP_ID, YEAR_ * I, PRODUCTION
Note that the table of constants may include several columns for commonly used cases. For example, and even though many of these can be expressed as mathematical expressions of numbers in a basic 0 to n sequence, one can have a column with only even number, another one with odd numbers, another one with multiples of 5 etc. Also if it small enough, no indexes are needed on a table of constants but these may become useful on a bigger on.
use this one:
WITH tn (n) as
(
SELECT 0
UNION ALL
SELECT n+1
FROM tn
WHERE tn.n < 10
)
SELECT DISTINCT t.CROP_ID, t.YEAR_ + (3*tn.n), t.PRODUCTION
FROM table t, tn
/*
WHERE tn.n < 10 ==> you will get 1 -> (10*3) + 3 = 33
*/