Repeat row based on the number in a column - sql

SqlFiddle Demo
I need to repeat each barcode of the article based on the quantity of this article in the table Stock.
This is source data:
| BarCode | quantity |
|---------|----------|
| 5142589 | 7 |
| 123454 | 5 |
| 1111145 | 3 |
I want result that looks like this:
Barcode
-------
5142589
5142589
5142589
5142589
5142589
5142589
5142589
123454
123454
123454
123454
123454
1111145
1111145
1111145
How can I do this?
Thanks

You can use table of numbers. Either permanent, or generated on the fly.
Query below uses CTE to generate up to 1000 numbers. Here is SQL Fiddle.
WITH
e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
) -- 10
,e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b) -- 10*10
,e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2) -- 10*100
,CTE_Numbers
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY n) AS Number
FROM e3
)
SELECT b.BarCode, s.quantity
FROM
TABLE_BARCODE b
INNER JOIN TABLE_STOCK s ON b.IdArticle = s.IdArticle
CROSS APPLY
(
SELECT TOP(s.quantity) CTE_Numbers.Number
FROM CTE_Numbers
ORDER BY CTE_Numbers.Number
) AS CA
Results:
| BarCode | quantity |
|---------|----------|
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 5142589 | 7 |
| 123454 | 5 |
| 123454 | 5 |
| 123454 | 5 |
| 123454 | 5 |
| 123454 | 5 |
| 1111145 | 3 |
| 1111145 | 3 |
| 1111145 | 3 |

You can get this by a simple recursive CTE.
WITH cte
AS
(
SELECT IdArticle,1 AS rn FROM TABLE_STOCK
UNION ALL
SELECT t.IdArticle,rn+1 AS rn
FROM cte c
INNER JOIN TABLE_STOCK t ON t.IdArticle = c.IdArticle and rn<t.QUANTITY
)
SELECT t.BarCode,TS.QUANTITY
FROM cte c
INNER JOIN TABLE_BARCODE t ON t.IdArticle = c.IdArticle
INNER JOIN TABLE_STOCK TS ON TS.IdArticle = C.IdArticle
ORDER BY t.IdArticle
Here is SQL Fiddle

Simplified and improved version of Vladmir's answer:
DECLARE #t table(BarCode int, quantity int)
INSERT #t values(5142589, 7),(123454, 5),(1111145,3)
;WITH
e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
) -- 10
,e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b) -- 10*10
,e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 ex) -- 100*100
SELECT BarCode
FROM #t t
CROSS APPLY
(
SELECT top(t.quantity) null dummy
FROM e3
) x

Related

how to get individual-clinic-month that are excluded from SQL query

I have the following dataset:
individual | clinic_1 | clinic_2 | month | address_recorded | address_code
1 | A | B | 01-01-2016 | 01-02-1999 | C01
1 | A | A | 01-01-2016 | 01-02-2003 | C02
1 | A | A | 01-01-2016 | 01-02-2001 | C06
1 | A | X | 01-01-2016 | 01-02-2000 | C03
2 | C | B | 01-04-2016 | 01-02-1999 | D04
2 | C | A | 01-04-2016 | 01-02-2001 | D05
2 | C | X | 01-04-2016 | 01-02-2000 | D06
I would like to get:
individual | clinic_1 | month | address_code
1 | A | 01-01-2016 | C02
2 | C | 01-04-2016 | D05
Criteria:
For unique set of individual-clinic_1-month with clinic_1 = clinic_2, select the most
recent date in which address was recorded within clinic_1
For unique set of individual-clinic_1-month with NO instances where clinic_1 = clinic_2,
select the most recent date in which address was recorded across
clinics
I thought about doing:
with cte_1
as
(
select * from table
where clinic_1 = clinic_2
)
,cte_2
as
(
select row_number () over (Partition by clinic_1, individual, month order by clinic_1, individual, month, address_recorded desc) as number, *
from cte_1
)
select individual, clinic_1, month, address_code from cte_2 where number = 1
But I don't know how to get those individual-clinic_1-month for which there are no instances where clinic_1=clinic_2, any ideas?
You can Union two select queries; one to select all records where clinic_1=clinic_2 and another one to select all records where clinic_1<>clinic_2
and clinic_1 not in the results set of the first query.
Both queries are grouped by [individual],[clinic_1], [clinic_2], [mnth] to find all of the required data rows for each [clinic_1] - [mnth] entry. Noting that for the 2nd query [clinic_2] is selected as ''.
Check the following:
with cte as
(SELECT [individual] ,[clinic_1],[clinic_2],[mnth],max([address_recorded]) as m
FROM [MyData] where [clinic_1]=[clinic_2]
group by [individual],[clinic_1],[clinic_2] ,[mnth]
),
cte2 as
(SELECT [MyData].[individual] ,[MyData].[clinic_1],'' as [clinic_2],[MyData].[mnth],max([MyData].[address_recorded]) as m
FROM [MyData]
Left Join cte on cte.individual=MyData.individual
and cte.mnth=MyData.mnth
where [MyData].[clinic_1]<>[MyData].[clinic_2] and cte.individual IS NULL
group by [MyData].[individual],[MyData].[clinic_1], [MyData].[mnth]
),
D as
(SELECT * FROM cte
UNION
SELECT * FROM cte2)
,
LastQr as(
select [MyData].individual, [MyData].clinic_1,[MyData].mnth,[MyData].address_code,
row_number() OVER(PARTITION BY [MyData].individual, [MyData].clinic_1,[MyData].mnth ORDER BY [MyData].individual, [MyData].clinic_1,[MyData].mnth)
as rn from D
INNER JOIN [MyData]
ON D.individual=MyData.individual and D.clinic_1=MyData.clinic_1 and D.mnth=MyData.mnth and D.m=MyData.address_recorded
and (D.clinic_2=MyData.clinic_2 or D.clinic_2='')
)
select * from LastQr where rn=1
See the results from dbfiddle.uk.

Postgres: Find missing items in a version table

I have a Table in Postgres 12 which tracks what Items i are use in which Versions v:
CREATE TABLE compare_test(v BIGINT, i BIGINT);
With example data:
INSERT INTO compare_test VALUES
(1,21),
(1,22),
(1,23),
(2,21),
(2,22),
(2,23),
(3,21),
(3,22);
I'm trying to create a View that returns:
source_v
target_v
source_i
target_i
1
3
23
null
2
3
23
null
Queries used to compare missing values in two Tables like:
SELECT l.v as source_v, l.i as source_i,
r.v as target_v, r.i as target_i
FROM compare_test l
LEFT JOIN
compare_test r ON r.i = l.i
WHERE r.i IS NULL;
and
SELECT l.v as source_v, l.i as source_i
FROM compare_test l
WHERE NOT EXISTS
(
SELECT i as target_i
FROM compare_test r
WHERE r.i = l.i
)
do not seem to work if the joined Table is the same Table or if more than 2 Versions are in the Table.
I don't have the option to change the Database Structure but I can use plugins.
The solution below gives those results.
It makes re-use of a CTE.
(but somehow I got a feeling that there should exist a more efficient way)
with cte1 as (
SELECT i
, count(*) cnt
, min(v) min_v
, max(v) max_v
FROM compare_test
GROUP BY i
)
, cte2 as
(
select *
from cte1 as c1
where not exists (
select 1
from cte1 c2
where c2.min_v = c1.min_v
and c2.max_v < c1.max_v
)
)
select distinct
t.v as source_v
, c1.max_v as target_v
, c2.i as source_i
, null as target_i
from cte2 c2
left join compare_test t
on t.i = c2.i
left join cte1 c1
on t.v between c1.min_v and c1.max_v
and c1.i != t.i
order by source_v
But if it's not really required to follow the relations, then it becomes really simple.
Then it's just a left join of the existing to all possible combinations.
select distinct
src.v as source_v
, missing.v as target_v
, src.i as source_i
, missing.i as target_i
from
(
select ver.v, itm.i
from (select distinct v from compare_test) as ver
cross join (select distinct i from compare_test) as itm
left join compare_test t
on t.v = ver.v and t.i = itm.i
where t.v is null
) as missing
left join compare_test as src
on src.i = missing.i and src.v != missing.v
order by target_i, target_v, source_v
source_v | target_v | source_i | target_i
-------: | -------: | -------: | -------:
1 | 5 | 21 | 21
2 | 5 | 21 | 21
3 | 5 | 21 | 21
1 | 5 | 22 | 22
2 | 5 | 22 | 22
3 | 5 | 22 | 22
1 | 3 | 23 | 23
2 | 3 | 23 | 23
1 | 5 | 23 | 23
2 | 5 | 23 | 23
5 | 1 | 44 | 44
5 | 2 | 44 | 44
5 | 3 | 44 | 44
db<>fiddle here

Recursive CTE (T-SQL) Returns Un-expected Result

I've been staring at this code for WAY too long, trying to figure out why my final query returns unexpected results.
Any help would be much appreciated. Thanks in advance.
Given the following code (running on SQL Server 2008 R2):
USE tempdb;
DECLARE #emp--loyee
TABLE (
EmployeeID int NOT NULL
,EmployeeName nvarchar(50) NOT NULL
PRIMARY KEY(EmployeeID)
)
INSERT INTO #emp
SELECT 1,'Fred'
UNION
SELECT 2,'Mary'
UNION
SELECT 3,'Joe'
UNION
SELECT 4,'Bill'
DECLARE #grp TABLE (
GroupID int NOT NULL
,GroupName nvarchar(50)
PRIMARY KEY(GroupID)
)
INSERT INTO #grp
SELECT 1,'Group 1'
UNION
SELECT 2,'Group 2'
UNION
SELECT 3,'Group 3'
DECLARE #empgrp TABLE (
EmployeeID int NOT NULL
,GroupID int NOT NULL
PRIMARY KEY (EmployeeID,GroupID)
)
INSERT INTO #empgrp
SELECT 1,1
UNION
SELECT 2,1
UNION
SELECT 3,1
UNION
SELECT 4,2
DECLARE #grpgrp TABLE (
GroupID int NOT NULL
,ParentGroupID int
,UNIQUE CLUSTERED(GroupID,ParentGroupID)
)
INSERT INTO #grpgrp
SELECT 1,2
UNION
SELECT 2,3;
WITH AllEmpGroups (EmployeeID,GroupID,RootGroupID)
AS
(
SELECT CAST(NULL as int) as EmployeeID,pgrp.GroupID,pgrp.ParentGroupID
FROM #grpgrp pgrp LEFT JOIN #grpgrp ggrp
ON pgrp.ParentGroupID = ggrp.GroupID
UNION ALL
SELECT e.EmployeeID,eg.GroupID,aeg.RootGroupID
FROM #emp e JOIN #empgrp eg
ON e.EmployeeID = eg.EmployeeID
JOIN #grpgrp ggrp
ON eg.GroupID = ggrp.GroupID
JOIN AllEmpGroups aeg
ON aeg.GroupID = ggrp.ParentGroupID
)
SELECT EmployeeID,GroupID,RootGroupID
FROM AllEmpGroups
What I get is:
+------------+---------+-------------+
| EmployeeID | GroupID | RootGroupID |
+------------+---------+-------------+
| NULL | 1 | 2 |
| NULL | 2 | 3 |
| 1 | 1 | 3 |
| 2 | 1 | 3 |
| 3 | 1 | 3 |
+------------+---------+-------------+
What I would expect/want to get is this:
+------------+---------+-------------+
| EmployeeID | GroupID | RootGroupID |
+------------+---------+-------------+
| NULL | 1 | 2 |
| NULL | 2 | 3 |
| 4 | 2 | 3 |
| 1 | 1 | 3 |
| 2 | 1 | 3 |
| 3 | 1 | 3 |
+------------+---------+-------------+
Bottom line, I want the full recursive stack of all employees beneath a given root group(s), with the root group id on every row.
What am I missing?
First:
You need a row for the root node in #grpgrp with values 3, null
The anchor (part before the union all) of your recursive cte needs to be the root node (3, null) for ancestor first recursion.
...
INSERT INTO #grpgrp
SELECT 1,2
UNION all
SELECT 2,3
UNION all
select 3, null;
WITH AllEmpGroups (EmployeeID,GroupID,RootGroupID)
AS
(
SELECT CAST(NULL as int) as EmployeeID,pgrp.GroupID, ParentGroupID = pgrp.GroupID
FROM #grpgrp pgrp LEFT JOIN #grpgrp ggrp
ON pgrp.ParentGroupID = ggrp.GroupID
where pgrp.ParentGroupId is null
UNION ALL
SELECT e.EmployeeID,eg.GroupID,aeg.RootGroupID
FROM #emp e JOIN #empgrp eg
ON e.EmployeeID = eg.EmployeeID
JOIN #grpgrp ggrp
ON eg.GroupID = ggrp.GroupID
JOIN AllEmpGroups aeg
ON aeg.GroupID = ggrp.ParentGroupID
)
SELECT EmployeeID,GroupID,RootGroupID
FROM AllEmpGroups
rextester demo: http://rextester.com/CBWY80387
returns:
+------------+---------+-------------+
| EmployeeID | GroupID | RootGroupID |
+------------+---------+-------------+
| NULL | 3 | 3 |
| 4 | 2 | 3 |
| 1 | 1 | 3 |
| 2 | 1 | 3 |
| 3 | 1 | 3 |
+------------+---------+-------------+
Beyond that, I would build the groups hierarchy first, then join the employees like so:
WITH AllEmpGroups (GroupID,ParentGroupID,RootGroupID)
AS
(
SELECT pgrp.GroupID, pgrp.ParentGroupID, RootGroupId = GroupID
FROM #grpgrp pgrp
where pgrp.ParentGroupId is null
UNION ALL
SELECT ggrp.GroupID,ggrp.ParentGroupID,aeg.RootGroupID
FROM #grpgrp ggrp
inner JOIN AllEmpGroups aeg
ON aeg.GroupID = ggrp.ParentGroupID
)
SELECT eg.EmployeeID,aeg.*
FROM AllEmpGroups aeg
left JOIN #empgrp eg
ON eg.GroupID = aeg.GroupID
rextester demo: http://rextester.com/FAK76354
returns:
+------------+---------+---------------+-------------+
| EmployeeID | GroupID | ParentGroupID | RootGroupID |
+------------+---------+---------------+-------------+
| NULL | 3 | NULL | 3 |
| 4 | 2 | 3 | 3 |
| 1 | 1 | 2 | 3 |
| 2 | 1 | 2 | 3 |
| 3 | 1 | 2 | 3 |
+------------+---------+---------------+-------------+
Start with
WITH AllGroups (RootGroupID,GroupID,ParentGroupID, level)
AS
(
SELECT GroupID RootGroupID, GroupID, Cast(NULL as int) ParentGroupID, 0 level
FROM #grp g
WHERE NOT EXISTS (SELECT 1 FROM #grpgrp gg WHERE gg.GroupID = g.GroupID)
UNION ALL
SELECT ag.RootGroupID, gg.GroupID, gg.ParentGroupID, level+1
FROM #grpgrp gg
JOIN AllGroups ag
ON ag.GroupID = gg.ParentGroupID
)
SELECT EmployeeID, ag.GroupID, ParentGroupID, RootGroupID
FROM AllGroups ag
LEFT JOIN #empgrp eg ON eg.GroupID = ag.GroupID
ORDER BY RootGroupID, level, ParentGroupID, GroupID;
Not sure why you need the row:
| NULL | 2 | 3 |

Extract row families from linked rows

I have a table of linked transactions similar to the following table
+----+----+----+
| # | A | B |
+----+----+----+
| 1 | 1 | 4 |
| 2 | 3 | 5 |
| 3 | 4 | 6 |
| 4 | 5 | 8 |
| 5 | 6 | 1 |
| 6 | 7 | 7 |
| 7 | 8 | 3 |
| 8 | 9 | 3 |
| 9 | 10 | 4 |
| 10 | 11 | 14 |
| 11 | 2 | 2 |
| 12 | 12 | 4 |
| 13 | 13 | 14 |
| 14 | 14 | 9 |
| 15 | 15 | 1 |
+----+----+----+
The numbers under columns A and B represent transaction Ids. So for instance, Transaction 1 is linked with transaction 4 by some criteria, tran 3 with tran 5, tran 4 with tran 6 and so on.
Transactions 2 and 7 aren't linked to any other transaction, hence they are self-linked.
What I want to extract are transaction families from this table- Since tran 1 and 4 are linked, tran 4 and 6 are linked, tran 10 and 4 are linked etc they come under one transacction family -(1,4,6,10,12,15).
I want to create families of transactions with the lowest transaction ID being the master transaction.
So ideally, the output will look like this
+----+------+--------------+
| # | Tran | Master_tran |
+----+------+--------------+
| 1 | 1 | 1 |
| 2 | 3 | 3 |
| 3 | 4 | 1 |
| 4 | 5 | 3 |
| 5 | 6 | 1 |
| 6 | 7 | 7 |
| 7 | 8 | 3 |
| 8 | 9 | 3 |
| 9 | 10 | 1 |
| 10 | 11 | 3 |
| 11 | 2 | 2 |
| 12 | 12 | 1 |
| 13 | 13 | 3 |
| 14 | 14 | 3 |
| 15 | 15 | 1 |
+----+------+----+
I have been toying with self-joins.
SELECT t1.a as x,
least (min(t1.b), min(t2.a)) as y
FROM test t1
LEFT JOIN test t2 on t2.b = t1.a
GROUP BY t1.a
ORDER BY t1.a asc
This code gives the following outupt
+------+----+---+
| Col1 | X | Y |
+------+----+---+
| 1 | 1 | 4 |
| 2 | 2 | 2 |
| 3 | 3 | 5 |
| 4 | 4 | 1 |
| 5 | 5 | 3 |
| 6 | 6 | 1 |
| 7 | 7 | 7 |
| 8 | 8 | 3 |
| 9 | 9 | 3 |
| 10 | 10 | |
| 11 | 11 | |
| 12 | 12 | |
| 13 | 13 | |
| 14 | 14 | 9 |
| 15 | 15 | |
+------+----+---+
I am not sure what is wrong in my code. Can someone point me in the right direction?
Thanks!
in principle you need a CONNECT BY Statement to solve such hierarchical problems.
While you have circular loops, you will also need a NOCYCLE clause, this will eliminate the last link in the loop, which is fine, as that link will never be part of the answer.
You also have links in both directions (f.e. (13, 14) and (14, 9)), so you must be careful to include that in your query (Twice!).
WITH t_order
AS (SELECT qt.qt_id, qt.qt_a, qt.qt_b, LEAST( qt.qt_a, qt.qt_b ) AS t_parent, GREATEST( qt.qt_a, qt.qt_b ) AS t_child
FROM query_test qt
UNION
SELECT qb.qt_id, qb.qt_a, qb.qt_b, GREATEST( qb.qt_a, qb.qt_b ) AS t_parent, LEAST( qb.qt_a, qb.qt_b ) AS t_child
FROM query_test qb)
, hier
AS (SELECT ps.qt_id
, ps.qt_a
, ps.qt_b
, t_parent
, t_child
, LEVEL
, CONNECT_BY_ROOT t_parent AS prev_tran
FROM t_order ps
CONNECT BY NOCYCLE PRIOR t_child = t_parent)
SELECT hr.qt_id, hr.qt_a, MIN( hr.prev_tran ) AS master_tran
FROM hier hr
GROUP BY hr.qt_id, hr.qt_a
ORDER BY hr.qt_id, hr.qt_a;
This will solve your problem, but might get very slow if those 100.000 records must be handled. The SQL statement also gets hard to understand if you need to combine this method with lots of other columns. For that you should factor out all qt.qt columns and join them in in the last select.
WITH t_order
AS (SELECT DISTINCT tran, root_tran
FROM (SELECT LEAST( qt.qt_a, qt.qt_b ) AS tran, GREATEST( qt.qt_a, qt.qt_b ) AS root_tran
FROM query_test qt
UNION
SELECT GREATEST( qb.qt_a, qb.qt_b ) AS tran, LEAST( qb.qt_a, qb.qt_b ) AS root_tran
FROM query_test qb))
, hier
AS (SELECT DISTINCT tran, root_tran
FROM (SELECT tran, CONNECT_BY_ROOT root_tran AS root_tran
FROM t_order
CONNECT BY NOCYCLE PRIOR tran = root_tran)
WHERE tran >= root_tran)
SELECT qt.qt_id
, qt.qt_a
, MIN( LEAST( h1.root_tran, h2.root_tran ) ) AS master_tran
FROM query_test qt
INNER JOIN hier h1 ON qt.qt_a = h1.tran
INNER JOIN hier h2 ON qt.qt_b = h2.tran
GROUP BY qt.qt_id, qt.qt_a
ORDER BY qt.qt_id, qt.qt_a;
I could not test this last statement.
I might have created that other solution.
Instead of using a CONNECT BY statement, you could also double your links, and redouble them any time that is needed.
The query to retrieve all links stays the same but it is followed by a simple query to replace the original links with all distinct combinations of two links.
Including the link that is formed by tran_a and tran_b, you have 2 + 1 + 2 links, so you can find paths up to 5 links long.
If that is to short, you insert an identical subquery under the previous subquery, and now it is 4 + 1 + 4 makes 9 links long.
As you see, your maximum pathlength doubles for each added subquery, with only moderately more performance costs.
First the query to check your demo data:
WITH double_0
AS (SELECT DISTINCT root_tran, tran
FROM ( SELECT LEAST( td_0.tran_a, td_0.tran_b ) AS root_tran
, GREATEST( td_0.tran_a, td_0.tran_b ) AS tran
FROM tran_demo td_0
UNION
SELECT GREATEST( qb.tran_a, qb.tran_b ) AS root_tran
, LEAST( qb.tran_a, qb.tran_b ) AS tran
FROM tran_demo qb ))
, double_1
AS (SELECT DISTINCT oa.root_tran, ob.tran
FROM double_0 oa INNER JOIN double_0 ob ON oa.tran = ob.root_tran)
SELECT td_1.td_id
, td_1.tran_a
, MIN( LEAST( d1.root_tran, d2.root_tran ) ) AS master_tran
FROM tran_demo td_1
INNER JOIN double_1 d1 ON td_1.tran_a = d1.tran
INNER JOIN double_1 d2 ON td_1.tran_b = d2.tran
GROUP BY td_1.td_id, td_1.tran_a
ORDER BY td_1.td_id, td_1.tran_a;
Then how you modify that:
Notice that you now query double_2 in the final query.
WITH double_0
AS (SELECT DISTINCT root_tran, tran
FROM ( SELECT LEAST( td_0.tran_a, td_0.tran_b ) AS root_tran
, GREATEST( td_0.tran_a, td_0.tran_b ) AS tran
FROM tran_demo td_0
UNION
SELECT GREATEST( qb.tran_a, qb.tran_b ) AS root_tran
, LEAST( qb.tran_a, qb.tran_b ) AS tran
FROM tran_demo qb ))
, double_1
AS (SELECT DISTINCT oa.root_tran, ob.tran
FROM double_0 oa INNER JOIN double_0 ob ON oa.tran = ob.root_tran)
, double_2
AS (SELECT DISTINCT oa.root_tran, ob.tran
FROM double_1 oa INNER JOIN double_0 ob ON oa.tran = ob.root_tran)
SELECT td_1.td_id
, td_1.tran_a
, MIN( LEAST( d1.root_tran, d2.root_tran ) ) AS master_tran
FROM tran_demo td_1
INNER JOIN double_2 d1 ON td_1.tran_a = d1.tran
INNER JOIN double_2 d2 ON td_1.tran_b = d2.tran
GROUP BY td_1.td_id, td_1.tran_a
ORDER BY td_1.td_id, td_1.tran_a;
Finally a query to check if the path length you're using ist still enough:
You already add the next level and subtract your current level.
As long as this query doesn't return any rows, the current query is correct.
WITH double_0
AS (SELECT DISTINCT root_tran, tran
FROM ( SELECT LEAST( td_0.tran_a, td_0.tran_b ) AS root_tran
, GREATEST( td_0.tran_a, td_0.tran_b ) AS tran
FROM tran_demo td_0
UNION
SELECT GREATEST( qb.tran_a, qb.tran_b ) AS root_tran
, LEAST( qb.tran_a, qb.tran_b ) AS tran
FROM tran_demo qb ))
, double_1
AS (SELECT DISTINCT oa.root_tran, ob.tran
FROM double_0 oa INNER JOIN double_0 ob ON oa.tran = ob.root_tran)
, double_2
AS (SELECT DISTINCT oa.root_tran, ob.tran
FROM double_1 oa INNER JOIN double_0 ob ON oa.tran = ob.root_tran)
SELECT td_1.tran_a
, MIN( LEAST( d1.root_tran, d2.root_tran ) ) AS master_tran
FROM tran_demo td_1
INNER JOIN double_2 d1 ON td_1.tran_a = d1.tran
INNER JOIN double_2 d2 ON td_1.tran_b = d2.tran
GROUP BY td_1.tran_a
MINUS
SELECT td_2.tran_a
, MIN( LEAST( d1.root_tran, d2.root_tran ) ) AS master_tran
FROM tran_demo td_2
INNER JOIN double_1 d1 ON td_2.tran_a = d1.tran
INNER JOIN double_1 d2 ON td_2.tran_b = d2.tran
GROUP BY td_2.tran_a
ORDER BY tran_a;
Performance testing you will have to do yourself.
I am optimistic while the subquery is cheap and each time the effective pathlength doubles.
Sooner or later this should become faster than the previous solution.
By the way, the remark about sorting the original links works here too!
Please mark my answer if it works.

Select a row X times

I have a very specific sql problem.
I have a table given with order positions (each position belongs to one order, but this isn't a problem):
| Article ID | Amount |
|--------------|----------|
| 5 | 3 |
| 12 | 4 |
For the customer, I need an export with every physical item that is ordered, e.g.
| Article ID | Position |
|--------------|------------|
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 12 | 1 |
| 12 | 2 |
| 12 | 3 |
| 12 | 4 |
How can I build my select statement to give me this results? I think there are two key tasks:
1) Select a row X times based on the amount
2) Set the position for each physical article
You can do it like this
SELECT ArticleID, n.n Position
FROM table1 t JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N 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) a
,(SELECT 0 AS N 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) b
) n
ON n.n <= t.amount
ORDER BY ArticleID, Position
Note: subquery n generates a sequence of numbers on the fly from 1 to 100. If you do a lot of such queries you may consider to create persisted tally(numbers) table and use it instead.
Here is SQLFiddle demo
or using a recursive CTE
WITH tally AS (
SELECT 1 n
UNION ALL
SELECT n + 1 FROM tally WHERE n < 100
)
SELECT ArticleID, n.n Position
FROM table1 t JOIN tally n
ON n.n <= t.amount
ORDER BY ArticleID, Position
Here is SQLFiddle demo
Output in both cases:
| ARTICLEID | POSITION |
|-----------|----------|
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 12 | 1 |
| 12 | 2 |
| 12 | 3 |
| 12 | 4 |
Query:
SQLFIDDLEExample
SELECT t1.[Article ID],
t2.number
FROM Table1 t1,
master..spt_values t2
WHERE t1.Amount >= t2.number
AND t2.type = 'P'
AND t2.number <= 255
AND t2.number <> 0
Result:
| ARTICLE ID | NUMBER |
|------------|--------|
| 5 | 1 |
| 5 | 2 |
| 5 | 3 |
| 12 | 1 |
| 12 | 2 |
| 12 | 3 |
| 12 | 4 |