Generating multiple data with SQL query - sql

I have 2 tables as below
Product_Asset:
PAId Tracks
1 2
2 3
Product_Asset_Resource:
Id PAId TrackNumber
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
I would like to know if I can generate the data in product_asset_resource table based on product_asset table using TSQL query (without complex cursor etc.)
For example, if the number of tracks in product_asset is 3 then I need to populate 3 rows in product_asset_resource with track numbers as 1,2,3

You can do this with the help of a Tally Table.
WITH E1(N) AS( -- 10 ^ 1 = 10 rows
SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
CteTally(N) AS(
SELECT TOP(SELECT MAX(Tracks) FROM Product_Asset)
ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
FROM E4
)
SELECT
Id = ROW_NUMBER() OVER(ORDER BY pa.PAId, t.N),
pa.PAId,
TrackNumber = t.N
FROM Product_Asset pa
INNER JOIN CteTally t
ON t.N <= pa.Tracks
ONLINE DEMO

Try this,I am not using any Tally Table
declare #Product_Asset table(PAId int,Tracks int)
insert into #Product_Asset values (1 ,2),(2, 3)
;with CTE as
(
select PAId,1 TrackNumber from #Product_Asset
union all
select pa.PAId,TrackNumber+1 from #Product_Asset pa
inner join cte c on pa.PAId=c.PAId
where c.TrackNumber<pa.Tracks
)
select ROW_NUMBER()over(order by paid)id, * from cte
IMHO,Recursive CTE or sub query or using temp table performance depend upon example to example.
I find Recursive CTE more readable and won't use them unless they exhibit performance problem.
I am not convince that Recursive CTE is hidden RBAR.
CTE is just syntax so in theory it is just a subquery
We can take any example to prove that using #Temp table will improve the performance ,that doesn't mean we always use temp table.
Similarly in this example using Tally Table may not improve the performance this do not imply that we should take help of Tally Table at all.

Related

Want multiple Rows from 1 Row

I have a table in a SQL Server like this (example):
ID
ARTICLE
ARTTEXT
COUNT
1
123456
Test1
5
2
324644
blabla
1
3
765456
nanana
12
Now these items are to be labelled. I.e. each copy needs a label. I then do this via the SSRS.
So I need from ID 1 5 labels, ID 2 1, ID 3 12.
Now the question is what does the select look like to get 5 rows from ID 1, 1 row from ID 2 and 12 rows from ID 3.
I guess a CTE, but it's not clear to me how to get x times the records
I look forward to your ideas.
I would use a Tally over the (far slower) rCTE solution. I use an inline tally here. If you need more than 100 rows, simply add more cross joins to N in the CTE defined as Tally (each cross join increases the maximum number of rows by a factor or 10).
CREATE TABLE dbo.YourTable (ID int,
Article int,
Arttext varchar(15),
[Count] int);
INSERT INTO dbo.YourTable
VALUES(1,123456,'Test1',5),
(2,324644,'blabla',1),
(3,765456,'nanana',12);
GO
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (SELECT MAX([Count]) FROM dbo.YourTable)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --100 rows, add more cross joins for more rows
SELECT YT.ID,
YT.Article,
YT.Arttext,
T.I AS [Count]
FROM dbo.YourTable YT
JOIN Tally T ON YT.[Count] >= T.I
ORDER BY YT.ID,
T.I;
GO
DROP TABLE dbo.YourTable;
If I understand correctly, you want to multiply the number of rows based on count. One method uses a recursive CTE:
with cte as (
select id, article, arttext, count
from t
union all
select id, article, arttext, count - 1
from cte
where count > 1
)
select id, article, arttext
from t;
If the count exceeds 100, then you want option (maxrecursion 0).

Insert unique value multiple times depending on variable column

I have the following columns in my table:
PromotionID | NumberOfCodes
1 10
2 5
I need to insert the PromotionID into a new SQL table a number of times - depending on the value in NumberOfCodes.
So given the above, my result set should read:
PromotionID
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
You need recursive cte :
with r_cte as (
select t.PromotionID, 1 as start, NumberOfCodes
from table t
union all
select id, start + 1, NumberOfCodes
from r_cte
where start < NumberOfCodes
)
insert into table (PromotionID)
select PromotionID
from r_cte
order by PromotionID;
Default recursion level 100, use query hint option(maxrecursion 0) if you have NumberOfCodes more.
I prefer a tally for such things. Once you start getting to larger row sets, the performance of an rCTe degrades very quickly:
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)) AS I
FROM N N1, N N2) --Up to 100 rows. Add more cross joins to N for more rows
SELECT YT.PromotionID
FROM dbo.YourTable YT
JOIN Tally T ON YT.NumberOfCodes >= T.I;
One option uses a recursive common table expression to generate the rows from the source table, that you can then insert in the target table:
with cte as (
select promotion_id, number_of_codes n from sourcetable
union all select promotion_id, n - 1 from mytable where n > 1
)
insert into targettable (promotion_id)
select promotion_id from cte

Finding missing numbers in a table

I'd like to generate a query that will produce a list of index values that are missing from a SQL table. So far, what I have is
SELECT index - 1
FROM table
WHERE index - 1 NOT IN (
SELECT DISTINCT index
FROM table)
AND
index != 1;
This only finds the first missing index of a sequence of missing indices. I'd like to produce a query which outputs every number between 1 and MAX(index) which does not appear as an index in the table. Is there a way to do that?
You need a numbers table to find the missing index values
Recursive CTE approach
DECLARE #max_num INT = (SELECT Max(index)
FROM table);
WITH cte
AS (SELECT 1 AS num
UNION ALL
SELECT num + 1
FROM cte
WHERE num < #max_num)
SELECT *
FROM cte c
WHERE NOT EXISTS (SELECT 1
FROM table t
WHERE c.num = t.index)
ORDER BY c.num
OPTION (maxrecursion 0)
Another approach using tally table to generate numbers.
;WITH E1(N)
AS (SELECT *
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) tc (N)), --10E+1 or 10 rows
E2(N)
AS (SELECT 1 FROM E1 a CROSS JOIN E1 b), --10E+2 or 100 rows
E4(N)
AS (SELECT 1 FROM E2 a CROSS JOIN E2 b), --10E+4 or 10,000 rows max
--E5(N) AS (SELECT 1 FROM E4 a CROSS JOIN E1 b), -- 10000 * 10 = 100000
cte(NUM)
AS (SELECT ItemNumber = Row_number()OVER(ORDER BY N) FROM E4)
SELECT *
FROM cte c
WHERE NOT EXISTS (SELECT 1
FROM table t
WHERE c.num = t.index)
ORDER BY c.num
Currently it generates 10000 sequential row numbers an if your max value is more than 10000 then you add another cross join to increase the numbers
My preference for this type of query is to show ranges. A simple way uses lead():
select (index + 1) as first_missing, (next_index - 1) as last_missing,
(next_index - index - 1) as num_missing
from (select t.*, lead(index) over (order by index) as next_index
from t
) t
where next_index <> index + 1;
This shows every "internal" missing range. Because it does not use a table of numbers, it works efficiently regardless of the size of the table or the number of missing values.
It does not show "missing" values at the beginning and end of the table. Your question does not specify that as an issue.

Returning while-loop values as multiple rows in SQL Server instead of multiple result sets

I have the following T-SQL, used to generate some random values:
DECLARE #cnt INT = 0;
WHILE #cnt < 100
BEGIN
select
Random_String =
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)
from
(select x='0123456789ABCDEFGHJKLMNPQRSTUWXYZ%#-=+') a
SET #cnt = #cnt + 1;
END;
This works well enough, except every string is returned as, what looks like, an independent result set.
Is there a way to refactor that query to return every value as one row in the same result set?
Environment is MS SQL Server 2008.
Thanks!
The best way to do this kind of thing is to forget about looping in t-sql. Using a numbers or tally table is a much better way to go about this.
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 cross join E1 b), --10E+2 or 100 rows
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E2
)
select
Random_String =
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)
from
(select x='0123456789ABCDEFGHJKLMNPQRSTUWXYZ%#-=+') a
cross join cteTally t
where t.N < = 100
Here's a way to do it using a tally table:
;With Tally (N) As
(
Select 0 Union All
Select 1 Union All
Select 2 Union All
Select 3 Union All
Select 4 Union All
Select 5 Union All
Select 6 Union All
Select 7 Union All
Select 8 Union All
Select 9
), Numbers (N) As
(
Select Row_Number() Over (Order By A.N) Num
From Tally A -- 10
Cross Join Tally B -- 100
Cross Join Tally C -- 1000
Cross Join Tally D -- 10000
Cross Join Tally E -- 100000
), LookupString (X) As
(
Select '0123456789ABCDEFGHJKLMNPQRSTUWXYZ%#-=+'
)
Select Random_String = substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)+
substring(x,(abs(checksum(newid()))%36)+1,1)
From LookupString
Cross Join Numbers
Where N <= 100

How to populate a table with 5 million rows in MS SQL Server?

I'm working on an app that should find 26-letter code in char(26) column out of 5,760,000 rows. I need to know how long it's going to take. I'm using MS SQL Server 2012 Express.
I have a database which has only one table, myTable:
Idcolumn integer
CodeColumn char(26)
DateAndTimeColumn datetime
Column 'CodeColumn' has an index.
IdColumn is simply integer ID.
CodeColumn has "00592098715648275649283746" format (this is an example).
DateAndTimeColumn is a timestamp.
I would like to populate this table with data to do some tests and to find out how long it is going to take to get an answer from the database. I don't know how to write proper tsql statement to populate my table with 5,760,000 rows. Especially that second column is very long. How can I populate the table to get my table populated?
Let's say the data should be like this when I use statement
SELECT IdColumn, CodeColumn, DateAndTimeColumn FROM myTable;
Output:
1 00000000000000000000000001 2014-11-19 15:46:50.843
2 00000000000000000000000002 2014-11-19 15:46:54.310
3 00000000000000000000000003 2014-11-19 15:46:56.060
and so on ... till 5,760,000 rows.
How can I do that?
;WITH Numbers AS
(
SELECT TOP (5760000)
IdColumn = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
CROSS JOIN sys.all_objects AS s3
)
INSERT INTO dbo.YourTable
SELECT IdColumn,
RIGHT(REPLICATE('0',26)+CONVERT(VARCHAR(26),IdColumn),26) CodeColumn,
GETDATE() DateAndTimeColumn
FROM Numbers;
Here is another way to do this using Lamak's excellent example. The only difference is this will create a 10 million row cte with zero reads. When you use sys.all_objects it can get extremely slow because of all the I/O.
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),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E6(N) AS (SELECT 1 from E4 a, E2 b, E1 c),
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E6
)
INSERT INTO dbo.YourTable
SELECT IdColumn,
RIGHT(REPLICATE('0',26)+CONVERT(VARCHAR(26),IdColumn),26) CodeColumn,
GETDATE() DateAndTimeColumn
FROM cteTally
where cteTally.N <= 5760000
Very similar to Lamak answer, this will not depends on your database structure:
;WITH Numbers AS
(
SELECT 1 AS id UNION SELECT 2 UNION SELECT 3 UNION
SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION
SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION
SELECT 10
),
Joins AS
(
SELECT TOP (5760000)
IdColumn = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[id]))
FROM Numbers AS s1 --10
CROSS JOIN Numbers AS s2 --100
CROSS JOIN Numbers AS s3 --1.000
CROSS JOIN Numbers AS s4 --10.000
CROSS JOIN Numbers AS s5 --100.000
CROSS JOIN Numbers AS s6 --1.000.000
CROSS JOIN Numbers AS s7 --10.000.000
)
INSERT INTO #YourTable
SELECT IdColumn,
RIGHT(REPLICATE('0',26)+CONVERT(VARCHAR(26),IdColumn),26) CodeColumn,
GETDATE() DateAndTimeColumn
FROM Joins;
It just generate a list of numbers from 1 to 10 and then it crossjoins all the way to 10^7.