SQL - How to get Treeview of GrandParent, Parent, Child by ID - sql

I have 2 tables, where Customer table has customer data and relations table has relations of customer.
CREATE Table Customer
(
id int,
name nvarchar(10)
)
INSERT INTO Customer Values
(1, 'aaa'),
(2, 'bbb'),
(3, 'ccc'),
(4, 'ddd'),
(5, 'eee'),
(6, 'fff'),
(7, 'ggg'),
(8, 'hhh'),
(9, 'iii'),
(10, 'jjj'),
(11, 'kkk'),
(12, 'lll')
CREATE TABLE Relations
(
id int,
parentid int
)
INSERT INTO Relations VALUES
(2, 1),
(3, 1),
(4, 2),
(5, 2),
(6, 1),
(7, 4),
(8, 5),
(9, 8),
(10, 8),
(12, 7)
I want to find GrandParent, Parent and child by ID. For Ex: If I want to find all relations of ID=4, I should get the result in the below format. Ordered by Grand Parent at the top if the ID has a parent or Grand parent. If not, then it has to show the child of that ID.
Grand Parent | aaa
Parent | bbb
Child | ggg
Child | lll
Could you please help me with the above query in "SQL Server".

You can use recursives CTEs (Common Table Expressions) to get this information. For example, for ID = 4 you can do:
with
a as ( -- get ancestors
select 0 as lvl, id, name from customer where id = 4
union all
select a.lvl - 1, c.id, c.name
from a
join relations r on a.id = r.id
join customer c on c.id = r.parentid
),
d as ( -- get descendants
select 0 as lvl, id, name from customer where id = 4
union all
select d.lvl + 1, c.id, c.name
from d
join relations r on d.id = r.parentid
join customer c on c.id = r.id
)
select lvl, id, name from a
union
select lvl, id, name from d
order by lvl
Result:
lvl id name
--- -- ----
-2 1 aaa
-1 2 bbb
0 4 ddd
1 7 ggg
2 12 lll

Related

Select rows using group by and in each group get column values based on highest of another column value

I need to get latest field based on another field in group by
we have
Table "SchoolReview"
Id
SchoolId
Review
Point
1
1
rv1
8
2
1
rv2
7
3
2
rv3
4
4
2
rv4
7
5
3
rv5
2
6
3
rv6
8
I need to group by SchoolId and the inside group I need to get Review and Point from highest "Id" column.
I dont need "Id" coulmn but even if I get it for this solution its okay.
Result I am looking for shall look like this.
SchoolId
Review
Point
1
rv2
7
2
rv4
7
3
rv6
8
Any one experienced in MS SQL Server can help in this regard?
Using sample data from other answer
SELECT *
INTO #Data
FROM (VALUES
(1, 1, 'rv1', 8),
(2, 1, 'rv2', 7),
(3, 2, 'rv3', 4),
(4, 2, 'rv4', 7),
(5, 3, 'rv5', 2),
(6, 3, 'rv6', 8)
) v (Id, SchoolId, Review, Point)
SELECT S.SchoolId,
S.Review,
S.Point
FROM #Data S
INNER JOIN
(
SELECT Id = MAX(S1.Id),
S1.SchoolId
FROM #Data S1
GROUP BY SchoolId
) X ON X.Id = S.Id AND X.schoolId = S.SchoolId
ORDER BY X.SchoolId
;
output
You do not need to group the rows, you simply need to select the appropriate rows from the table. In this case, using ROW_NUMBER() is an option:
Table:
SELECT *
INTO Data
FROM (VALUES
(1, 1, 'rv1', 8),
(2, 1, 'rv2', 7),
(3, 2, 'rv3', 4),
(4, 2, 'rv4', 7),
(5, 3, 'rv5', 2),
(6, 3, 'rv6', 8)
) v (Id, SchoolId, Review, Point)
Statement:
SELECT SchoolId, Review, Point
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY SchoolId ORDER BY Id DESC) AS Rn
FROM Data
) t
WHERE Rn = 1
Result:
SchoolId Review Point
---------------------
1 rv2 7
2 rv4 7
3 rv6 8

SQL Query for Select Sequence Numbers

SQL Query for Select Sequence Numbers
In SQL server, I want to select rows based on sequence numbers. For example I am having data as below:
ID RowNos
A 1
B 2
X NULL
C 4
D 5
Y NULL
E 7
F 8
G 9
H 11
I 13
Query Should return
ID NextID
A B -- Since RowNos 1,2 is in sequence
C D -- Since RowNos 4,5 is in sequence
E G -- Since RowNos 7,8,9 is in sequence
I don't have idea to start this query. Otherwise I'll post my trial too.
DECLARE #t TABLE (ID CHAR(1), RowNos INT)
INSERT INTO #t
VALUES
('A', 1), ('B', 2), ('X', NULL),
('C', 4), ('D', 5), ('Y', NULL),
('E', 7), ('F', 8), ('G', 9),
('H', 11), ('I', 13)
SELECT MIN(ID), MAX(ID)
FROM (
SELECT *, rn = ROW_NUMBER() OVER (ORDER BY RowNos)
FROM #t
) t
WHERE RowNos IS NOT NULL
GROUP BY RowNos - rn
HAVING MIN(ID) != MAX(ID)
Output:
---- ----
A B
C D
E G
to select them ordered should be something like:
SELECT * FROM table_name WHERE RowNos IS NOT NULL ORDER BY RowNos ASC;

SQL query to reconstruct inherited EAV model

I have 5 tables in my database representing an inherited EAV model:
CREATE TABLE AttributeNames
("ID" int, "Name" varchar(8))
;
INSERT INTO AttributeNames
("ID", "Name")
VALUES
(1, 'Color'),
(2, 'FuelType'),
(3, 'Doors'),
(4, 'Price')
;
CREATE TABLE MasterCars
("ID" int, "Name" varchar(10))
;
INSERT INTO MasterCars
("ID", "Name")
VALUES
(5, 'BMW'),
(6, 'Audi'),
(7, 'Ford')
;
CREATE TABLE MasterCarAttributes
("ID" int, "AttributeNameId" int, "Value" varchar(10), "MasterCarId" int)
;
INSERT INTO MasterCarAttributes
("ID", "AttributeNameId", "Value", "MasterCarId")
VALUES
(100, 1, 'Red', 5),
(101, 2, 'Gas', 5),
(102, 3, '4', 5),
(102, 4, '$100K', 5),
(103, 1, 'Blue', 6),
(104, 2, 'Diesel', 6),
(105, 3, '3', 6),
(106, 4, '$80k', 6),
(107, 1, 'Green', 7),
(108, 2, 'Diesel', 7),
(109, 3, '5', 7),
(110, 4, '$60k', 7)
;
CREATE TABLE LocalCars
("ID" int, "MasterCarId" int)
;
INSERT INTO LocalCars
("ID", "MasterCarId")
VALUES
(8, '5'),
(9, '6'),
(10, NULL)
;
CREATE TABLE LocalCarAttributes
("ID" int, "AttributeNameId" int, "Value" varchar(6), "LocalCarId" int)
;
INSERT INTO LocalCarAttributes
("ID", "AttributeNameId", "Value", "LocalCarId")
VALUES
(43, 1, 'Yellow', 8),
(44, 3, '6', 9),
(45, 1, 'Red', 10),
(46, 2, 'Gas', 10),
(47, 3, '2', 10),
(48, 4, '$60k', 10)
;
I can retrieve all of master car attributes as follows:
SELECT MC.ID, MCA.AttributeNameId, MCA.Value
FROM MasterCars MC
left join MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
order by MC.ID;
Likewise, I can retrieve all of the local car attributes as follows:
SELECT LC.ID, LCA.AttributeNameId, LCA.Value
FROM LocalCars LC
left join LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
order by LC.ID;
If LocalCars.MasterCarId is not NULL, then that local car can inherit the attributes of that master car. A local car attribute with the same AttributeNameId overrides any master attribute with the same AttributeNameId.
So given the data above, I have 3 local cars each with 4 attributes (color, fuelType, doors, price). Inherited attribute values in bold:
Local Car Id = 1 (Yellow, Gas, 4, $100K)
Local Car Id = 2 (Blue, Diesel, 6, $80k)
Local Car Id = 3 (Red, Gas, 2, $60k)
I'm trying to find the necessary joins required to join the two queries above together to give a complete set of local cars attributes, some inherited:
LocalCarId AttributeNameId Value
------------------------------------------
1 1 Yellow
1 2 Gas
1 3 4
1 4 $100K
2 1 Blue
2 2 Diesel
2 3 6
2 4 $80K
3 1 Red
3 2 Gas
3 3 2
3 4 $60K
or possibly even:
LocalCarId AttributeNameId LocalValue MasterValue
-------------------------------------------------------------
1 1 Yellow Red
1 2 NULL Gas
1 3 NULL 4
1 4 NULL $100K
2 1 NULL Blue
2 2 NULL Diesel
2 3 6 3
2 4 NULL $80K
3 1 Red NULL
3 2 Gas NULL
3 3 2 NULL
3 4 $60K NULL
The problem can be solved by performing a union on all of your local car attributes and master car attributes. Each record is marked with an [IsMasterAttribute] flag. The next step is then use the ROW_NUMBER() window function to rank each of the duplicate attributes. The final step is to only select attributes which has a rank of 1.
;WITH CTE_CombinedAttributes
AS
(
SELECT 1 AS IsMasterAttribute
,LC.ID
,MC.ID AS MasterCarId
,MCA.AttributeNameId
,MCA.Value
FROM MasterCars MC
LEFT OUTER JOIN MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
INNER JOIN LocalCars LC ON LC.MasterCarId = MC.ID
UNION ALL
SELECT 0 AS IsMasterAttribute
,LC.ID
,LC.MasterCarId
,LCA.AttributeNameId
,LCA.Value
FROM LocalCars LC
LEFT OUTER JOIN LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
)
,
CTE_RankedAttributes
AS
(
SELECT [IsMasterAttribute]
,[ID]
,[AttributeNameId]
,[Value]
,ROW_NUMBER() OVER (PARTITION BY [ID], [AttributeNameId] ORDER BY [IsMasterAttribute]) AS [AttributeRank]
FROM CTE_CombinedAttributes
)
SELECT [IsMasterAttribute]
,[ID]
,[AttributeNameId]
,[Value]
FROM CTE_RankedAttributes
WHERE [AttributeRank] = 1
ORDER BY [ID]
The second output is also possible by performing a simple pivot on the final result:
;WITH CTE_CombinedAttributes
AS
(
SELECT 1 AS IsMasterAttribute
,LC.ID
,MC.ID AS MasterCarId
,MCA.AttributeNameId
,MCA.Value
FROM MasterCars MC
LEFT OUTER JOIN MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
INNER JOIN LocalCars LC ON LC.MasterCarId = MC.ID
UNION ALL
SELECT 0 AS IsMasterAttribute
,LC.ID
,LC.MasterCarId
,LCA.AttributeNameId
,LCA.Value
FROM LocalCars LC
LEFT OUTER JOIN LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
)
,
CTE_RankedAttributes
AS
(
SELECT [IsMasterAttribute]
,[ID]
,[AttributeNameId]
,[Value]
,ROW_NUMBER() OVER (PARTITION BY [ID], [AttributeNameId] ORDER BY [IsMasterAttribute]) AS [AttributeRank]
FROM CTE_CombinedAttributes
)
SELECT [ID]
,[AttributeNameId]
,MAX(
CASE [IsMasterAttribute]
WHEN 0 THEN [Value]
END
) AS LocalValue
,MAX(
CASE [IsMasterAttribute]
WHEN 1 THEN [Value]
END
) AS MasterValue
FROM CTE_RankedAttributes
GROUP BY [ID], [AttributeNameId]
ORDER BY [ID]
SQL Fiddle Demo
SELECT LC."ID" as LocalCarID,
COALESCE(LCA."AttributeNameId", MCA."AttributeNameId") as "AttributeNameId",
COALESCE(LCA."Value", MCA."Value") as "Value"
FROM LocalCars LC
LEFT JOIN MasterCars MC
ON LC."MasterCarId" = MC."ID"
LEFT JOIN MasterCarAttributes MCA
ON MC."ID" = MCA."MasterCarId"
LEFT JOIN LocalCarAttributes LCA
ON ( MCA."AttributeNameId" = LCA."AttributeNameId"
OR MCA."AttributeNameId" IS NULL)
-- This is the important part
-- Try to join with a MasterAtribute otherwise use the Car Atribute.
AND LC."ID" = LCA."ID"
OUTPUT
| LocalCarID | AttributeNameId | Value |
|------------|-----------------|--------|
| 1 | 1 | Blue |
| 1 | 2 | Gas |
| 2 | 1 | Green |
| 2 | 2 | Diesel |

Need a query to insert 'level' into an adjacent list

I have a table like so
ID Node ParentID
1 A 0
2 B 1
3 C 1
4 D 2
5 E 2
6 F 3
7 G 3
8 H 3
9 I 4
10 J 4
11 K 10
12 L 11
I need a query to generate a 'level' field that shows how many levels deep a particular node is. Example below
ID Node ParentID Level
1 A 0 1
2 B 1 2
3 C 1 2
4 D 2 3
5 E 2 3
6 F 3 4
7 G 3 4
8 H 3 4
9 I 4 5
10 J 4 5
11 K 10 6
12 L 11 7
Select Id,
Node,
ParentID,
Dense_Rank() Over(Order by ParentID) as Level
from Table_Name
SQL Fiddle Demo
You can use DENSE_RANK function
SELECT i.ID, p.Node, i.ParentID
,Dense_Rank() Over(Order by ParentID) as Level
FROM TableName AS i;
for more detail visit: http://blog.sqlauthority.com/2007/10/09/sql-server-2005-sample-example-of-ranking-functions-row_number-rank-dense_rank-ntile/
I think the correct way to do it will be to get the parent level and increment it by 1 when inserting the data since all other ways are expensive performance wise.
Something like:
;with tree (ID, ParentID, Level)
as (
select ID, ParentID, 1 from TableName where ParentID = 0
union all
select t.ID, t.ParentID, 1 + tree.Level
from Tree join TableName t on t.ParentID = Tree.ID
)
select ID, Level from Tree
Try this
CREATE TABLE #Table1
([ID] int, [Node] varchar(1), [ParentID] int)
;
INSERT INTO #Table1
([ID], [Node], [ParentID])
VALUES
(1, 'A', 0),
(2, 'B', 1),
(3, 'C', 1),
(4, 'D', 2),
(5, 'E', 2),
(6, 'F', 3),
(7, 'G', 3),
(8, 'H', 3),
(9, 'I', 4),
(10, 'J', 4),
(11, 'K', 10),
(12, 'L', 11)
;
;WITH CTE ([ID], [ParentID], [Node], [Level])
as (
SELECT [ID], [ParentID], [Node], 1 FROM #Table1 WHERE ParentID = 0
UNION all
select t.[ID], t.[ParentID], t.[Node], 1 + c.[Level]
from CTE c inner join #Table1 t ON t.[ParentID] = c.[ID]
)
select ID, [Node], [ParentID], [Level] from CTE
ORDER BY [Node]
DROP TABLE #Table1
Here, you need to set level by grouping ParentID then join both tables by ParentID.
WITH CTE (ParentID, Level)
AS (
SELECT ParentID
, Row_Number() OVER (ORDER BY ParentID) AS Level
FROM Table1
GROUP BY ParentID
)
SELECT t1.ID, t1.Node, t1.ParentID, CTE.Level
FROM Table1 t1
JOIN CTE ON t1.ParentID = CTE.ParentID;
See this SQLFiddle
Update: (for MySQL - just to help others)
To do the same in MySQL try to get row number like this:
SELECT t1.ID, t1.Node, t1.ParentID, Tbl.Level
FROM Table1 t1
JOIN
(
SELECT #Level:=#Level+1 AS Level , ParentID
FROM (SELECT DISTINCT ParentID FROM Table1) t
, (SELECT #Level:=0) r
ORDER BY ParentID
) Tbl
ON t1.ParentID = Tbl.ParentID;
See this SQLFiddle

Fetch duplicate records as well as Unique record in ssis

I have table mentioned below (id and Loc are Primary Keys)
ID LOC RNK NBR1 NBR2
1 2 A 10 b --->
3 4 A 10 b --->
5 6 A 11 C
8 2 A 12 D
6 3 A 10 b --->
SO here I have to fetch only duplicate records according to NBR1 and NBR2, It should fetch all the records not only the duplicates(marked as --->).
If I understood your question correctly you can do it with a subquery
CREATE TABLE #Test (ID int, LOC int, RNK char(1), NBR1 int, NBR2 char(1) )
INSERT INTO #Test VALUES
(1, 2, 'A', 10, 'b'),
(3, 4, 'A', 10, 'b'),
(5, 6, 'A', 11, 'C'),
(8, 2, 'A', 12, 'D'),
(6, 3, 'A', 10, 'b')
SELECT *
FROM #Test t1
WHERE EXISTS
(SELECT 1
FROM #Test t2
WHERE t1.NBR1 = t2.NBR1
AND t1.NBR2 = t2.NBR2
GROUP BY NBR1, NBR2
HAVING COUNT(1) > 1)
You can also use this, but cost will be more. The RowsCount having values greater than 1 are duplicate and having values 1 are unique records.
With Temp As
(
Select ID,LOC,RNK,NBR1,NBR2,Row_NUMBER() OVER (PARTITION BY NBR2 ORDER BY NBR1) AS ROWSCOUNT FROM <<TABLE_NAME>>
)
Select * from Temp