Multiple rows by number in cell - sql-server

I have table multiple:
CREATE TABLE multiple
(
id int,
Param1 int,
Param2 int,
Param3 int,
Param4 int
);
INSERT INTO multiple VALUES
(1, 1, 2, 3, 0),
(2, 3, 1, 0, 0),
(3, 1, 2, 2, 1);
SELECT * FROM multiple;
I want to achieve with t-SQL new table like this: (Number of rows = Value in Param attributes)
Counter ID TYPE
1 1 Param1
2 1 Param2
3 1 Param2
4 1 Param3
5 1 Param3
6 1 Param3
7 2 Param1
8 2 Param1
9 2 Param1
10 2 Param2
11 3 Param1
12 3 Param2
13 3 Param2
14 3 Param3
15 3 Param3
16 3 Param4
I adder here this text because I have error in editor that it looks like my post is mostly code;

Hey you can use following code to achieve your goal :
CREATE TABLE multiple
(
id int,
Param1 int,
Param2 int,
Param3 int,
Param4 int
);
INSERT INTO multiple VALUES
(1, 1, 2, 3, 0),
(2, 3, 1, 0, 0),
(3, 1, 2, 2, 1);
SELECT * FROM multiple;
CREATE TABLE multiple1
(
ID int,
TYPE VARCHAR(10)
)
DECLARE #id int = 0
WHILE (#id<=(SELECT Count(id) FROM multiple))
BEGIN
DECLARE #Count int = 0
DECLARE #Count1 int = 0
DECLARE #Count2 int = 0
DECLARE #Count3 int = 0
WHILE(#Count<(SELECT Param1 FROM multiple where id = #id))
BEGIN
INSERT INTO multiple1 VALUES ((Select id from multiple where id = #id),'Param1')
SET #Count = #Count + 1
END
WHILE(#Count1<(SELECT Param2 FROM multiple where id = #id))
BEGIN
INSERT INTO multiple1 VALUES ((Select id from multiple where id = #id),'Param2')
SET #Count1 = #Count1 + 1
END
WHILE(#Count2<(SELECT Param3 FROM multiple where id = #id))
BEGIN
INSERT INTO multiple1 VALUES ((Select id from multiple where id = #id),'Param3')
SET #Count2 = #Count2 + 1
END
WHILE(#Count3<(SELECT Param4 FROM multiple where id = #id))
BEGIN
INSERT INTO multiple1 VALUES ((Select id from multiple where id = #id),'Param4')
SET #Count3 = #Count3 + 1
END
SET #id = #id + 1
END
SELECT * FROM multiple1

Have you heard of "tally tables"? They'll make quick work of this. Here is an example solution that uses one. It works by joining on each column where the number is <= the value in the Param attributes, giving you the number of rows you need. Then the id and type are selected.
Note this example uses a tally table that only counts up to 256, so if you need more, you can add an extra level - just be sure to put your tally table into a temp table otherwise it will go from taking a really fast time to execute, to being a really slow time.
CREATE TABLE multiple
(
id int,
Param1 int,
Param2 int,
Param3 int,
Param4 int
);
INSERT INTO multiple VALUES
(1, 1, 2, 3, 0),
(2, 3, 1, 0, 0),
(3, 1, 2, 2, 1);
SELECT * FROM multiple;
-- Tally table known as a "Ben-Gan" style Tally
WITH lv0 AS (SELECT 0 g UNION ALL SELECT 0)
,lv1 AS (SELECT 0 g FROM lv0 a CROSS JOIN lv0 b) -- 4
,lv2 AS (SELECT 0 g FROM lv1 a CROSS JOIN lv1 b) -- 16
,lv3 AS (SELECT 0 g FROM lv2 a CROSS JOIN lv2 b) -- 256
,Tally (n) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM lv3)
SELECT
ROW_NUMBER() OVER (ORDER BY Id) [Counter],
Id,
[Type]
FROM
(
SELECT Id, 'Param1' AS [Type] FROM multiple INNER JOIN Tally ON multiple.Param1 >= Tally.n UNION ALL
SELECT Id, 'Param2' AS [Type] FROM multiple INNER JOIN Tally ON multiple.Param2 >= Tally.n UNION ALL
SELECT Id, 'Param3' AS [Type] FROM multiple INNER JOIN Tally ON multiple.Param3 >= Tally.n UNION ALL
SELECT Id, 'Param4' AS [Type] FROM multiple INNER JOIN Tally ON multiple.Param4 >= Tally.n
) [single]

Related

SQL Find Gaps in ranges with exclusions and regroup them as new ranges

I have two SQL tables.
One contains the file information say Table1
Second contains record of print say Table2
**Table1**
File_name TotalPages
====================
A 50
B 75
C 50
**Table2**
File_name from_page to_page
============================
A 13 15
A 21 30
B 13 13
A 41 41
The requirement is to display ranges of pending pages in the file whenever the user select the file.
For e.g.:
The user should see the below table say table3 when user wants to see pending record of file_name A
File_name from_page to_page
===========================
A 1 12
A 16 20
A 30 40
A 42 50
I read the below SQL Gaps and Island but couldn't find a way.
SQL Gaps and Islands
This query needs SQL 2012 or higher version. IF you have lower version use row_number to order #Table2 and join with next row.
declare #Table1 table (
File_name char(1)
, TotalPages int
)
declare #Table2 table (
File_name char(1)
, from_page int
, to_page int
)
insert into #Table1
values
('A', 50)
,('B', 75)
,('C', 50)
insert into #Table2
values
('A', 13, 15)
,('A', 21, 30)
,('B', 13, 13)
,('A', 41, 41)
select
File_name, from_page = to_page + 1
, to_page = next_row - iif(TotalPages = next_row, 0, 1)
from (
select
a.*, b.TotalPages
, next_row = isnull(lead(a.from_page) over (partition by a.File_name order by a.from_page), b.TotalPages)
from
#Table2 a
join #Table1 b on a.File_name = b.File_name
) t
where
next_row - to_page > 1
union all
select
File_name, 1, min(from_page) - 1
from
#Table2
group by File_name
having min(from_page) > 1
order by 1, 2
Output:
File_name from_page to_page
---------------------------------
A 1 12
A 16 20
A 31 40
A 42 50
B 1 12
B 14 75
Edit:
Something like this should work:
;with cte as (
select
*, rn = row_number() over (partition by File_name order by from_page)
from
#Table2
)
select
File_name, from_page = to_page + 1
, to_page = next_row - iif(TotalPages = next_row, 0, 1)
from (
select
a.*, c.TotalPages
, next_row = isnull(b.from_page, c.TotalPages)
from
cte a
left join cte b on a.File_name = b.File_name and a.rn + 1 = b.rn
join #Table1 c on a.File_name = c.File_name
) t
where
next_row - to_page > 1
union all
select
File_name, 1, min(from_page) - 1
from
#Table2
group by File_name
having min(from_page) > 1
order by 1, 2
To do this you have to create one reference table which have numbers from 1 to max no of pages into you file. and update this table to achieve your result. below is the sample code -
create table Table1 (File_name char(1) ,TotalPages int)
insert into Table1
select 'A' , 50
union
select 'B',75
union
select 'C',50
create table Table2 (File_name char(1) ,from_page int , to_page int )
insert into Table2
select 'A' , 13,15
union
select 'A',21,30
union
select 'B',13,13
union
select 'A',41,41
go
if exists ( select OBJECT_ID('tempdb.dbo.#Ref'))
begin
drop table #Ref
end
create table #Ref(i int , IsValue int , FileName char(1))
declare #File_name char(1) , #TotalPages int , #pagecount int
set #File_name = 'A' -------------<<< set your file name
select #TotalPages = TotalPages , #pagecount = 1 from Table1
while (#pagecount <= #TotalPages)
begin
insert into #Ref(i ,FileName)
select #pagecount , #File_name
set #pagecount = #pagecount+ 1
end
--select * from #Ref
go
update #Ref
set IsValue = 1
where i in (select i from #Ref cross join Table2
where i >=from_page and i <= to_page) ---- update the pages which are available
declare #File_name char(1) , #TotalPages int , #pagecount int , #isVal int = 2
select #TotalPages = TotalPages , #pagecount = 1 from Table1
while (#pagecount <= #TotalPages)
begin
if exists( select * from #Ref where i = #pagecount and IsValue is not null)
begin
set #isVal = #isVal + 1
end
update #Ref
set IsValue = #isVal
where i = #pagecount
and IsValue is null
set #pagecount = #pagecount + 1
end
select FileName ,MIN(i)from_page , MAX(i) to_page from #Ref
where IsValue <>1
group by FileName ,IsValue

Merge Segment Ranges Based on Grouped Value

I have a table of segments with a beginning point, an ending point, and a value like so:
Bmp | Emp | SomeVal
0 1 1
1 2 1
2 3 2
3 4 2
4 5 1
I would like to merge (summarize) these records so they look like so:
Bmp | Emp | SomeVal
0 2 1
2 4 2
4 5 1
I've simplified my data set for the purpose of this question. The end result is I need unique rows grouped by the SomeVal column (in my real data set, there are about 20 columns) with the segments stitched together from Bmp to Emp, but not overlapping.
I've tried the following:
DECLARE #tbl TABLE (Bmp int, Emp int, SomeVal int)
INSERT INTO #tbl
SELECT 0, 1, 1 UNION
SELECT 1, 2, 1 UNION
SELECT 2, 3, 2 UNION
SELECT 3, 4, 2 UNION
SELECT 4, 5, 1
SELECT MIN(Bmp) AS Bmp, Max(Emp) AS Emp, SomeVal FROM #tbl
GROUP BY SomeVal
Unfortunately, it comes out like so which is wrong:
Bmp | Emp | SomeVal
0 5 1
2 4 2
My query above only works if the values of SomeVal do not repeat. How can I fix my sql?
Minimum required version is SQL 2008.
You may using ROW_NUMBER() function to correlate begin group row with end group row.
DECLARE #tbl TABLE (Bmp int, Emp int, SomeVal int)
INSERT INTO #tbl
SELECT 0, 1, 1 UNION
SELECT 1, 2, 1 UNION
SELECT 2, 3, 2 UNION
SELECT 3, 4, 2 UNION
SELECT 4, 5, 1
;WITH
[Begins] AS
(
SELECT Bmp, SomeVal, ROW_NUMBER() OVER (ORDER BY Bmp) AS OrderNumber
FROM #tbl AS [Begin]
WHERE NOT EXISTS (
SELECT 1
FROM #tbl AS [Prev]
WHERE [Prev].Emp = [Begin].Bmp AND [Prev].SomeVal = [Begin].SomeVal)
),
[Ends] AS
(
SELECT Emp, SomeVal, ROW_NUMBER() OVER (ORDER BY Emp) AS OrderNumber
FROM #tbl AS [End]
WHERE NOT EXISTS (
SELECT 1
FROM #tbl AS [Next]
WHERE [Next].Bmp = [End].Emp AND [Next].SomeVal = [End].SomeVal)
)
SELECT [Begins].Bmp, [Ends].Emp, [Begins].SomeVal
FROM [Begins]
INNER JOIN [Ends]
ON [Begins].OrderNumber = [Ends].OrderNumber;
Here is another option...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
Bmp INT NOT NULL PRIMARY KEY CLUSTERED,
Emp INT NOT NULL,
SomeVal INT NOT NULL
);
INSERT #TestData (Bmp, Emp, SomeVal)
SELECT 0, 1, 1 UNION ALL
SELECT 1, 2, 1 UNION ALL
SELECT 2, 3, 2 UNION ALL
SELECT 3, 4, 2 UNION ALL
SELECT 4, 5, 1;
--==============================================
WITH
cte_GroupStart AS (
SELECT
td.Bmp,
td.Emp,
td.SomeVal,
GroupStart = CASE WHEN td.SomeVal = LAG(td.SomeVal, 1) OVER (ORDER BY td.Bmp) THEN NULL ELSE ROW_NUMBER() OVER (ORDER BY td.Bmp) END
FROM
#TestData td
),
cte_FillGroup AS (
SELECT
gs.Bmp,
gs.Emp,
gs.SomeVal,
AggGroup = MAX(gs.GroupStart) OVER (ORDER BY gs.Bmp) -- ROWS UNBOUNDED PRECEDING is implied and should work as expected 2008
FROM
cte_GroupStart gs
)
SELECT
Bmp = MIN(fg.Bmp),
Emp = MAX(fg.Emp),
fg.SomeVal
FROM
cte_FillGroup fg
GROUP BY
fg.AggGroup,
fg.SomeVal
ORDER BY
fg.AggGroup;

SQL Server counting number of rows from beginning of sorted table where colvalue = 0

We have SQL Server 2008 R2. I have table MyTable.
I need:
sort by column Nr (ORDER BY MyTable.NR)
count how many values are equal to 0 from the beginning of this sorted table (taking in mind that it is already sorted by NR) (MyTable.Value = 0).
For instance, if MyTable has these values:
NR Value
2 0
1 0
3 5
4 0
then I have to get count = 2, because if sort this table by Nr we have two rows where Value = 0 from the beginning of the table.
You can do this using NOT EXISTS to exclude all rows where a row exists with a lower NR and a Value <> 0. e.g.
DECLARE #T TABLE (NR INT, Value INT);
INSERT #T VALUES (2, 0), (1, 0), (3, 5), (4, 0);
SELECT COUNT(*)
FROM #T AS t1
WHERE t1.Value = 0
AND NOT EXISTS
( SELECT 1
FROM #T AS t2
WHERE t2.Value <> 0
AND t2.NR < t1.NR
);
You can filter data using predicate Value = 0, then number filtered data with row_number, and then count rows, where assigned number equal to original one:
declare #data table (NR int, Value int);
insert into #data values
(2, 0), (1, 0), (3, 5), (4, 0);
;with
step1 as (select NR from #data where Value = 0),
step2 as (select NR, rn = row_number() over (order by NR) from step1)
select count(1)
from step2
where NR = rn;

SQL Server 2008 : TSQL, select same data for different times based on column value

I am using mssql 2008 R2,
i have below structure
create table #temp (
product int,
[order] int,
ord_qnty int
)
insert #temp
select 10 ,3,4
now, if ord_qnty is 4 , i want to select same product,order four times but in all four rows thevalue of ord_qnty should be 1 , i.e.
out put should be
Product order ord_qnty
10 3 1
10 3 1
10 3 1
10 3 1
If you have a numbers table, you can use that. If not, you can generate one:
;with Numbers(n) as (
select ROW_NUMBER() OVER (ORDER BY object_id) from sys.objects
)
select product,[order],1 as ord_qnty
from #temp t inner join Numbers num
on t.ord_qnty >= num.n
(In my nearly empty scratch database, the ROW_NUMBER() generates 77 rows. If that's not going to be enough, you can introduce cross-joins or use other tricks to generate more numbers, or you can create and populate a permanent numbers table)
Try this one -
Query:
DECLARE #temp TABLE
(
product INT
, [order] INT
, ord_qnty INT
)
INSERT #temp(product, [order], ord_qnty)
SELECT 10, 3, 4
SELECT
t.product
, t.[order]
, ord_qnty = 1
FROM #temp t
JOIN [master].dbo.spt_values sv ON t.ord_qnty > sv.number
WHERE sv.[type] = 'p'
SELECT
t.product
, t.[order]
, ord_qnty = 1
FROM #temp t
JOIN (
SELECT number = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM sys.system_parameters p
) sv ON t.ord_qnty >= sv.number
Output:
product order ord_qnty
----------- ----------- -----------
10 3 1
10 3 1
10 3 1
10 3 1
Query Cost:
For any "millions value":
SET NOCOUNT ON;
DECLARE #numbers TABLE (number INT)
DECLARE #temp TABLE
(
product INT
, [order] INT
, ord_qnty INT
)
INSERT #temp(product, [order], ord_qnty)
SELECT 10, 3, 4
DECLARE
#i BIGINT = 1
, #max BIGINT = (
SELECT MAX(ord_qnty)
FROM #temp
)
WHILE (#i <= #max) BEGIN
INSERT INTO #numbers (number)
VALUES (#i), (#i+1), (#i+2), (#i+3), (#i+4), (#i+5), (#i+6), (#i+7), (#i+8), (#i+9)
SELECT #i += 10
END
SELECT
t.product
, t.[order]
, ord_qnty = 1
FROM #temp t
CROSS JOIN (
SELECT *
FROM #numbers
WHERE number < #max + 1
) t2

How to find the parent ID in a return table

I have following table:
ID ParentID
1 NULL
2 1
3 2
4 NULL
5 4
6 5
7 3
I want to find the first ID of a specific child ID.
Example: ID=7 and the result is 1
ID=6 and the result is 4
How to do it?
You need to do a bit of recursive CTE magic to solve this one.
Given the data in a table variable thusly:
declare #data table(id int, parentid int)
insert into #data
select 1, null
union select 2,1
union select 3,2
union select 4, null
union select 5,4
union select 6,5
union select 7,3
The following should do the trick:
;with recursiveTable as
(
select d.id, d.parentId, 0 as depth
from #data d
where d.id=6 -- Parameterize this
union all
select d.id, d.parentid, r.depth-1
from #data d
inner join recursiveTable r
on d.id = r.parentId
)
select top 1 id
from recursiveTable
order by depth
Plugging 6 in as above returns 4. Changing this to 7 returns 1 as requested.
Try this:
CREATE TABLE childpar(ID int,ParentID int)
INSERT INTO childpar
values(1,NULL),
(2, 1),
(3, 2),
(4, NULL),
(5, 4),
(6, 5),
(7, 3)
DECLARE #inpyID int
SET #inpyID=7
;WITH CTE1 as (
select * from childpar where id=#inpyID
union all
select c2.* from CTE1 c1 inner join childpar c2 on c1.ParentID = c2.ID
)
select top 1 id from CTE1 order by id asc

Resources