How can I expand out a row into multiple row result set? - sql

I have a table that I'm trying to break out each row into one or more rows based on the second column value. Like this:
table (id, pcs):
ABC 3
DEF 1
GHJ 4
query result (id, pcs_num):
ABC 1
ABC 2
ABC 3
DEF 1
GHJ 1
GHJ 2
GHJ 3
GHJ 4
I'm writing this as a sproc in SQL server 2008. My best solution is to use a cursor and add [pcs] number of rows to a temp table for each row in the table. Is seems like there must be a simpler solution than this that I am missing. Thanks.

You can use a recursive CTE:
;WITH CTE AS
(
SELECT *
FROM YourTable
UNION ALL
SELECT id, pcs-1
FROM CTE
WHERE pcs-1 >= 1
)
SELECT *
FROM CTE
ORDER BY id, pcs
OPTION(MAXRECURSION 0)
Here is a demo for you to try.

Here is my approach. Extremely easy with a Tally Table (A table that only has a column with a value 1 -> X). No need for recursion, and this will be much faster over larger tables.
Notice we are only making a Tally Table of 100 rows, feel free to expand that as large as you'd like. If you get too crazy, you might need another cross join in sys.sysobjects to accomdate. The real query is at the bottom, as you can see it's extremely easy.
SELECT TOP 100
IDENTITY( INT,1,1 ) AS N
INTO #Tally
FROM sys.sysobjects sc1 ,
sys.sysobjects sc2
CREATE TABLE #Test
(
Id char(3),
pcs int
)
INSERT INTO #Test
SELECT 'ABC', 3 UNION ALL
SELECT 'DEF', 1 UNION ALL
SELECT 'GHJ', 4
SELECT #Test.Id, #Tally.N FROM #Tally
JOIN #Test ON #Tally.N <= #Test.pcs
ORDER BY #Test.Id

SELECT
id
,pcs_num
FROM MyTable
CROSS APPLY (
SELECT TOP (pcs)
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) pcs_num
FROM master.dbo.spt_values
) t

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

Run a query with out a subquery to yield results

'MotorVehicles' Table
I ran a query to find 'AVG(Price) * 2' of attached table, then I ran another query where I substituted 'AVG(Price) * 2' with a hard number, I was able to get the two records in the result table, I have tried to use the aggregate functions in a 'Having' clause but my result table comes back empty. Need some help I would like to formulate a SELECT statement without a subquery to find all Motor vehicles whos price is more or equal to 'AVG(Price * 2)' in attached table.
thanks in advance
Many methods, this isn't nice, but it would work:
;with cte_a as
(
select avg(Price)*2 [Average]
from yourTable
-- or whatever your query to get average is as long as only 1 result
)
select *
from yourTable yt
inner join cte_a a on 1 = 1
where price >= a.Average
select * from MotorVehicles where price > (select avg(price) from t)*2;
I apologise if this is the subquery you want to avoid.
You can get something similar using a partitioned AVG().
DECLARE #T TABLE
(
X INT
)
INSERT #T SELECT 1
INSERT #T SELECT 10
INSERT #T SELECT 15
INSERT #T SELECT 20
SELECT X,XAVG=AVG(X) OVER(PARTITION BY 1 ) FROM #T
Resulting in:
X XAVG
1 11
10 11
15 11
20 11

How to create a idendity for each id

Is it possible to create a composite key in sql 2000
code id
abc 1
abc 2
abc 3
def 1
def 2
ghi 1
where the id restarts the count at each change of code. I need the numbering to be exactly like that either by creating a table or other SELECT statement trickery.
how to do this in sql server 2000
Need Query Help
Here is one way to retrieve this data at runtime, without having to actually store it in the table, which is incredibly cumbersome to try and maintain. I'm using a #temp table here but you can pretend #a is your permanent table. As is, this will support up to 256 duplicates. If you need more, it can be adjusted.
CREATE TABLE #a(code VARCHAR(32));
INSERT #a SELECT 'abc'
UNION ALL SELECT 'abc'
UNION ALL SELECT 'abc'
UNION ALL SELECT 'def'
UNION ALL SELECT 'def'
UNION ALL SELECT 'ghi';
GO
SELECT x.code, id = y.number FROM
(
SELECT code, maxid = COUNT(*) FROM #a GROUP BY code
) AS x
CROSS JOIN
(
SELECT DISTINCT number FROM master..spt_values
WHERE number BETWEEN 1 AND 256
) AS y
WHERE x.maxid >= y.number;
DROP TABLE #a;
You can try this
INSERT INTO TABLENAME (code, id) VALUES( 'code',
(Select ISNULL(MAX(id), 0) FROM TableName where code = 'code')+1)

select a set of values as a column without CREATE

I'm trying to write a query that will return all QUERY_ID values alongside all matching TABLE_ID values, where QUERY_ID is not specified in any table, and I can't create tables, so have to specify it in the query itself:
QUERY_ID TABLE_ID
1 1
2 NULL
3 3
4 4
5 NULL
I feel like there ought to be a simple way to do this, but I can't think of it for the life of me. Any help would be wonderful. Thanks!
select q.QUERY_ID, t.TABLE_ID
from (
select 1 as QUERY_ID
union all
select 2
union all
select 3
union all
select 4
union all
select 5
) q
left outer join MyTable t on q.QUERY_ID = t.TABLE_ID
one way by using the built in master..spt_values table
SELECT number AS QUERY_ID,TABLE_ID
FROM master..spt_values v
LEFT JOIN YourTable y ON y.QUERY_ID = y.TABLE_ID
WHERE TYPE = 'p'
AND number > 0
AND number <= (SELECT COUNT(*) FROM YourTable)
order by QUERY_ID
are you able to create #temp tables...can you do this?
create table #temp(QUERY_ID int identity,TABLE_ID varchar(200))
insert #temp(TABLE_ID)
select TABLE_ID
from YourTable
select * from #temp
order by QUERY_ID
drop table #temp
or like this
select identity(int,1,1) as QUERY_ID,TABLE_ID
into #temp
from YourTable
select * from #temp
order by QUERY_ID
On sql server 2005 and up there is the row_number function so maybe a reason to upgrade :-)