Order parent child records by parent group and children - sql-server

I need sort query results by specific two related columns. My table is:
Row no | Col 1 | Col 2 | Col 3 | Col 4
1 | 1 | X | 1 | 5
2 | 2 | Y | 1 | 6
3 | 5 | Z | 2 | 7
4 | 6 | T | 2 | 0
5 | 7 | T | 3 | 0
6 | 6 | W | 2 | 0
The values in Col 4 represents the child record linked to Col 1.
So for Row no = 1 the next child record is row 3, where Col 1 holds the value of Col 4 from the first row.
The next child row for row 3 is row 5, based on the link between Col 1 and Col 4.
And I'd like to return this results:
Row no | Col 1 | Col 2 | Col 3 | Col 4
1 | 1 | X | 1 | 5
3 | 5 | Z | 2 | 7
5 | 7 | T | 3 | 0
2 | 2 | Y | 1 | 6
4 | 6 | T | 2 | 0
6 | 6 | W | 2 | 0
So I want the ordering to show a Parent row, followed by it's child rows, before moving on to the next top level Parent row.

You can achieve what you're after with a Recursive CTE to find all the parent records and link them to their child records.
Dummy table setup:
CREATE TABLE #Table1
(
[Row no] INT ,
[Col 1] INT ,
[Col 2] VARCHAR(1) ,
[Col 3] INT ,
[Col 4] INT
);
INSERT INTO #Table1
( [Row no], [Col 1], [Col 2], [Col 3], [Col 4] )
VALUES ( 1, 1, 'X', 1, 5 ),
( 2, 2, 'Y', 1, 6 ),
( 3, 5, 'Z', 2, 7 ),
( 4, 6, 'T', 2, 0 ),
( 5, 7, 'T', 3, 0 ),
( 6, 6, 'W', 2, 0 );
Recursive CTE:
;WITH cte
AS ( SELECT * ,
ROW_NUMBER() OVER ( ORDER BY t1.[Col 1] ) GroupNo
FROM #Table1 t1
WHERE t1.[Col 1] NOT IN ( SELECT [Col 4] FROM #Table1 )
UNION ALL
SELECT t.* ,
cte.GroupNo
FROM #Table1 t
INNER JOIN cte ON cte.[Col 4] = t.[Col 1]
)
SELECT *
FROM cte
ORDER BY cte.GroupNo , cte.[Row no]
DROP TABLE #Table1
This combines 2 queries with a UNION ALL. The first query finds the top level items where the value of [Col 1] does not appear in [Col 4]:
WHERE t1.[Col 1] NOT IN ( SELECT [Col 4] FROM #Table1 )
The second query finds the child records on the first query with this JOIN:
INNER JOIN cte ON cte.[Col 4] = t.[Col 1]
For the ordering, I've used the following to give the the results of the first query a GroupNo, which is used later to order the records:
ROW_NUMBER() OVER ( ORDER BY t1.[Col 1] ) GroupNo

Related

T-SQL ordering data in hierarchy query from parents to children

I have hierarchical data with parents and children together with positions.
Sample:
create table groups
(
Child varchar(1),
Parent varchar(1),
Position int
);
insert into groups (Child, Parent, Position)
values ('A', NULL, 1),
('B', 'A', 1),
('C', 'B', 1),
('D', 'B', 2),
('E', NULL, 2),
('F', NULL, 3),
('G', 'F', 1),
('H', 'G', 1),
('I', 'G', 2),
('J', 'F', 2),
('K', 'J', 1),
('L', 'J', 2);
Each group and subgroup has it own position starting with 1, e.g.:
all parents have positions: 1 - x
all children with parent "A" have positions: 1 - x
all children with parent "N" have positions: 1 - x
I need to order the query the way that the children of the parents always come between parents ascending, for example (added levels for better imagination):
Level|Child|Parent|Position
-----+-----+------+---------
3 | A | NULL | 1
2 | B | A | 1
1 | C | B | 1
1 | D | B | 2
3 | E | NULL | 2
3 | F | NULL | 3
2 | G | F | 1
1 | H | G | 1
1 | I | G | 2
2 | J | F | 2
1 | K | J | 1
1 | L | J | 2
This is my current code:
;with cte as
(
select Child, Parent, Position
from groups
union all
select t.Child, t.Parent, t.Position
from groups t
join cte on t.Child = cte.Parent
)
select distinct *
from cte
order by Position
db<>fiddle example
Thank you
Your cte is a little messed up. Here's how I would do it:
;with cte as (
select Child, Parent, Position As ParentPosition, Position, 1 As level
from groups
where parent is null
union all
select t.Child, t.Parent, cte.ParentPosition, t.Position, level + 1
from groups AS t
join cte on t.Parent = cte.Child
)
select *
from cte
order by ParentPosition, level, Position
Your cte's anchor part was all the records in the table, where in fact it should be just the records that has no parents (hence the where parent is null).
Also, I've added a column for the parent's position to enable sorting all the children of the same parent before the next parent, and I've added a level column to help order the children by distance from the main parent.
Results:
Child Parent ParentPosition Position level
A 1 1 1
B A 1 1 2
C B 1 1 3
D B 1 2 3
E 2 2 1
F 3 3 1
G F 3 1 2
J F 3 2 2
K J 3 1 3
H G 3 1 3
I G 3 2 3
L J 3 2 3
Db<>Fiddle

Summarizing a column in SQL Server after the creation of Pivot table

I cannot summarize numbers in the table (SQL-Server) after pivoting and I will be very grateful for your advice.
Better if I explain the problem on the example:
Existing table:
+-------+-----------+-----------+-------------------+
| # | $$$$$ | Fire | Water |
+-------+-----------+-----------+-------------------+
| 1 | 5 | 1 | 5 |
| 1 | 4 | 1 | 5 |
| 1 | 10 | 1 | 5 |
| 2 | 3 | 3 | 8 |
| 2 | 4 | 3 | 8 |
+-------+-----------+-----------+-------------------+
Desired output:
+-------+-----------+-----------+-------------------+
| # | $$$$$ | Fire | Water |
+-------+-----------+-----------+-------------------+
| 1 | 19 | 1 | 5 |
| 2 | 7 | 3 | 8 |
+-------+-----------+-----------+-------------------+
I tend to believe that I already tried all the solutions I found with summarizing and grouping by, but it was not solved, so I rely on you. Thanks in advance. The code I used to create the table:
WITH Enerc AS
(
SELECT
a1.[#],
a1.[$$$$$],
a2.[cause_of_loss]
FROM
data1 AS a1
LEFT JOIN
data2 AS a2 ON a1.[id] = a2.[id]
)
SELECT *
FROM Enerc
PIVOT
(SUM(gross_claim) FOR [cause_of_loss] IN ([Fire], [Water])) AS PivotTable;
No need to pivot. Your desired result should be got by grouping and using SUM:
SELECT
a1.[#],
SUM(a1.[$$$$$]),
a1.[Fire]
a1.[Water]
from data1 as a1
group by a1.[#], a1.[Fire], a1.[Water]
Let me show an example:
DECLARE #Hello TABLE
(
[#] INT,
[$$$$$] INT,
[Fire] INT,
[Water] INT
)
INSERT INTO #Hello
(
#,
[$$$$$],
Fire,
Water
)
VALUES
( 1, -- # - int
5, -- $$$$$ - int
1, -- Fire - int
5 -- Water - int
)
, (1, 4, 1, 5)
, (1, 10, 1, 5)
, (2, 3, 3, 8)
, (2, 4, 3, 8)
SELECT
h.#,
SUM(h.[$$$$$]),
h.Fire,
h.Water
FROM #Hello h
GROUP BY h.#, h.Fire, h.Water
try group by after the pivot.
With Enerc as
(SELECT
a1.[#],
a1.[$$$$$],
a2.[cause_of_loss]
from data1 as a1
left join data2 as a2
on a1.[id] = a2.[id]
)
select *
into tmp
from Enerc
PIVOT
(sum(gross_claim)
FOR [cause_of_loss] in (
[Fire], [Water]))
as PivotTable
select [#], sum([$$$$$])as [$$$$$], Fire, Water
from #tmp
group by [#],Fire, Water
EDIT: in case of permission denied:
With Enerc as
(SELECT
a1.[#],
a1.[$$$$$],
a2.[cause_of_loss]
from data1 as a1
left join data2 as a2
on a1.[id] = a2.[id]
),phase2 as(
select *
from Enerc
PIVOT
(sum(gross_claim)
FOR [cause_of_loss] in (
[Fire], [Water]))
as PivotTable)
select [#], sum([$$$$$])as [$$$$$], Fire, Water
from phase2
group by [#],Fire, Water

SQL Server : nonstandard query

I have a table dzialki_wlasciciele
id_dzialki | id_wlasc | id_malz |
1 | 1 | 2
1 | 2 | 1
2 | 1 | 2
3 | 1 | 2
Now I need two queries - one to return two rows where id_dzialki = 1 (where it has two id_wlasc where id_wlasc of second row = id_malz of first row)
And second query should return id_dzialki = 2 and = 3 (where there is no second record where id_wlasc = id_malz)
Second question I have :
select *
from dzialki_wlasciciele dw1
where dw1.ID_WLASC = 1
and dw1.id_dzialki not in (select dw2.id_dzialki
from dzialki_wlasciciele dw2
where ID_malz = 1)
But how about first question ?
If I understand correctly:
DECLARE #dzialki_wlasciciele TABLE
(
id_dzialki INT ,
id_wlasc INT ,
id_malz INT
)
INSERT INTO #dzialki_wlasciciele
VALUES ( 1, 1, 2 ),
( 1, 2, 1 ),
( 2, 1, 2 ),
( 3, 1, 2 )
SELECT d1.*
FROM #dzialki_wlasciciele d1
JOIN #dzialki_wlasciciele d2 ON d2.id_dzialki = d1.id_dzialki
AND d2.id_wlasc = d1.id_malz
WHERE d1.id_dzialki = 1
Output:
id_dzialki id_wlasc id_malz
1 1 2
1 2 1

Sorting columns to be multi-row

I have data in SQL Server like this:
floor | Apartment
1 1
1 2
1 3
2 4
2 5
2 6
because one floor has 3 apartments, I want to sort or convert the Apartment column to be a row like the flowing
4 | 5 | 6
1 | 2 | 3
SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table YourTable
(
Floor int,
Apartment int
)
go
insert into YourTable values
( 1, 1),
( 1, 2),
( 1, 3),
( 2, 4),
( 2, 5),
( 2, 6)
Query 1:
select P.Floor,
P.[1] as Room1,
P.[2] as Room2,
P.[3] as Room3
from (
select Floor,
Apartment,
row_number() over(partition by Floor order by Apartment) as rn
from YourTable
) as T
pivot(min(T.Apartment) for T.rn in ([1], [2], [3])) as P
Results:
| FLOOR | ROOM1 | ROOM2 | ROOM3 |
|-------|-------|-------|-------|
| 1 | 1 | 2 | 3 |
| 2 | 4 | 5 | 6 |

Column into rows and make a master table of columns

I need insert columns data to a different table and create master table for columns
Like example : I need to convert table tblcatData into two tables tblCat and tblcatDataNew
tblCatData
Primaykey | A | B | C | D | D | F | G | H | I | J | K | L | M |
--------------------------------------------------------------------
1 | 1 | 2 | 3 | 5 | 5 | 5 | 3 | 3 | 3 | 1 | 4 | 1 | 1 |
2 | 1 | 2 | 5 | 5 | 5 | 5 | 3 | 5 | 3 | 1 | 1 | 5 | 1 |
3 | 5 | 2 | 3 | 5 | 5 | 5 | 5 | 3 | 3 | 1 | 1 | 1 | 4 |
tblCat
PrimaryKey | Category
----------------------------
1 | A
2 | B
3 | C
4 | D
5 | E
6 | F
7 | G
. .
. .
. .
tblCatDataNew
PrimaryKey | FK_CatID | Data |
-----------------------------------
1 | 1 | 1 |
2 | 1 | 1 |
3 | 1 | 5 |
4 | 2 | 2 |
5 | 2 | 2 |
6 | 2 | 2 |
7 | 3 | 3 |
8 | 3 | 5 |
. . .
. . .
. . .
You could try the following scenario:
Create tblCat.
Create tblCatDataNew with the following deviations from the original design:
the FK_CatID column is allowed to accept NULLs temporarily (or maybe permanently, if that was your original intention);
an extra column is added temporarily to receive the category names from the original table.
Unpivot tblCatData and insert the results into tblCatDataNew (the values into Data and the column names, as category names, into the temporary column).
Select all the distinct category names from tblCatDataNew and insert them into tblCat. (That will produce the key values for them.)
Update the foreign keys in tblCatDataNew from tblCat, joining the two tables by category names.
Drop the temporary column from tblCatDataNew.
Set tblCatDataNew.FK_CatID as NOT NULL (that is, if you wanted it to be so).
Here's the entire test script, including creation of the original table (in case someone would like to try it):
BEGIN TRANSACTION
GO
/* prepare the original table, for tests */
WITH data (
Primaykey, A, B, C, D, E, F, G, H, I, J, K, L, M
) AS (
SELECT 1 , 1, 2, 3, 5, 5, 5, 3, 3, 3, 1, 4, 1, 1 UNION ALL
SELECT 2 , 1, 2, 5, 5, 5, 5, 3, 5, 3, 1, 1, 5, 1 UNION ALL
SELECT 3 , 5, 2, 3, 5, 5, 5, 5, 3, 3, 1, 1, 1, 4
)
SELECT * INTO tblCatData FROM data;
GO
/* Step 1 */
CREATE TABLE tblCat (
PrimaryKey int IDENTITY CONSTRAINT PK_tblCat PRIMARY KEY,
Category varchar(50) NOT NULL
);
GO
/* Step 2 */
CREATE TABLE tblCatDataNew (
PrimaryKey int IDENTITY CONSTRAINT PK_tblCatDataNew PRIMARY KEY,
FK_CatID int NULL CONSTRAINT FK_tblCatDataNew_tblCat FOREIGN KEY REFERENCES tblCat (PrimaryKey),
Data int,
Category varchar(50)
);
GO
/* Step 3 */
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data,
Category
FROM tblCatData
UNPIVOT (
Data for Category IN (A, B, C, D, E, F, G, H, I, J, K, L, M)
) u
ORDER BY
Category,
Primaykey;
GO
/* Step 4 */
INSERT INTO tblCat (Category)
SELECT DISTINCT Category
FROM tblCatDataNew
GO
/* Step 5 */
UPDATE tblCatDataNew
SET FK_CatID = c.PrimaryKey
FROM tblCat c
WHERE tblCatDataNew.Category = c.Category
GO
/* Step 6 */
ALTER TABLE tblCatDataNew
DROP COLUMN Category
GO
/* Step 7 */
ALTER TABLE tblCatDataNew
ALTER COLUMN FK_CatID int NOT NULL
GO
/* view the results */
SELECT * FROM tblCat
SELECT * FROM tblCatDataNew
GO
ROLLBACK TRANSACTION
Note that the UNPIVOT clause is supported in SQL Server starting from the 2005 version. In earlier versions you'd have to use a different method to unpivot data (Step 3), e.g. like this:
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data = CASE x.CatNum
WHEN 1 THEN A
WHEN 2 THEN B
WHEN 3 THEN C
WHEN 4 THEN D
WHEN 5 THEN E
WHEN 6 THEN F
WHEN 7 THEN G
WHEN 8 THEN H
WHEN 9 THEN I
WHEN 10 THEN J
WHEN 11 THEN K
WHEN 12 THEN L
WHEN 13 THEN M
END,
Category = CASE x.CatNum
WHEN 1 THEN 'A'
WHEN 2 THEN 'B'
WHEN 3 THEN 'C'
WHEN 4 THEN 'D'
WHEN 5 THEN 'E'
WHEN 6 THEN 'F'
WHEN 7 THEN 'G'
WHEN 8 THEN 'H'
WHEN 9 THEN 'I'
WHEN 10 THEN 'J'
WHEN 11 THEN 'K'
WHEN 12 THEN 'L'
WHEN 13 THEN 'M'
END
FROM tblCatData
CROSS JOIN (
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 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12 UNION ALL
SELECT 13
) x (CatNum)
ORDER BY
Category,
Primaykey;
or even like this:
INSERT INTO tblCatDataNew (
Data,
Category
)
SELECT
Data = CASE x.CatNum
WHEN 1 THEN A
WHEN 2 THEN B
WHEN 3 THEN C
WHEN 4 THEN D
WHEN 5 THEN E
WHEN 6 THEN F
WHEN 7 THEN G
WHEN 8 THEN H
WHEN 9 THEN I
WHEN 10 THEN J
WHEN 11 THEN K
WHEN 12 THEN L
WHEN 13 THEN M
END,
Category = x.CatName
FROM tblCatData
CROSS JOIN (
SELECT 1, 'A' UNION ALL
SELECT 2, 'B' UNION ALL
SELECT 3, 'C' UNION ALL
SELECT 4, 'D' UNION ALL
SELECT 5, 'E' UNION ALL
SELECT 6, 'F' UNION ALL
SELECT 7, 'G' UNION ALL
SELECT 8, 'H' UNION ALL
SELECT 9, 'I' UNION ALL
SELECT 10, 'J' UNION ALL
SELECT 11, 'K' UNION ALL
SELECT 12, 'L' UNION ALL
SELECT 13, 'M'
) x (CatNum, CatName)
ORDER BY
Category,
Primaykey;
Here are the results that the above script produced for me:
tblCat:
PrimaryKey Category
----------- --------------------------------------------------
1 A
2 B
3 C
4 D
5 E
6 F
7 G
8 H
9 I
10 J
11 K
12 L
13 M
tblCatDataNew:
PrimaryKey FK_CatID Data
----------- ----------- -----------
1 1 1
2 1 1
3 1 5
4 2 2
5 2 2
6 2 2
7 3 3
8 3 5
9 3 3
10 4 5
11 4 5
12 4 5
13 5 5
14 5 5
15 5 5
16 6 5
17 6 5
18 6 5
19 7 3
20 7 3
21 7 5
22 8 3
23 8 5
24 8 3
25 9 3
26 9 3
27 9 3
28 10 1
29 10 1
30 10 1
31 11 4
32 11 1
33 11 1
34 12 1
35 12 5
36 12 1
37 13 1
38 13 1
39 13 4
You can use union to unpivot the columns, and select ... into to store the result in a new table:
select PrimaryKey
, FK_CatId
, Category
into tblCatDataNew
from (
select PrimaryKey
, 1
, A
from tblCatData
union all
select PrimaryKey
, 2
, B
from tblCatData
union all
...
)

Resources