SQL Server + Select only two records for each masterID in a table - sql

I got a child table that contains 1 to n records linked to the master table via a MasterID column.
How can I select from the child table only the first 5 records for each MasterID?

Using Sql Server CTE and ROW_NUMBER you could try using
DECLARE #ParentTable TABLE(
ID INT
)
INSERT INTO #ParentTable SELECT 1
INSERT INTO #ParentTable SELECT 2
INSERT INTO #ParentTable SELECT 3
DECLARE #ChildTable TABLE(
ID INT,
ParentID INT
)
INSERT INTO #ChildTable SELECT 1, 1
INSERT INTO #ChildTable SELECT 2, 1
INSERT INTO #ChildTable SELECT 3, 1
INSERT INTO #ChildTable SELECT 4, 1
INSERT INTO #ChildTable SELECT 5, 1
INSERT INTO #ChildTable SELECT 6, 1
INSERT INTO #ChildTable SELECT 7, 1
INSERT INTO #ChildTable SELECT 8, 2
INSERT INTO #ChildTable SELECT 9, 2
INSERT INTO #ChildTable SELECT 10, 3
INSERT INTO #ChildTable SELECT 11, 3
;WITH RowNums AS(
SELECT pt.ID ParentID,
ct.ID ChildID,
ROW_NUMBER() OVER (PARTITION BY pt.ID ORDER BY ct.ID) RowNum
FROM #ParentTable pt INNER JOIN
#ChildTable ct ON pt.ID = ct.ParentID
)
SELECT ParentID,
ChildID
FROM RowNums
WHERE RowNum <= 5

Try a regular join where the constraint is a subquery pulling the TOP 5 from the child table. In untested pseudocode:
SELECT A.MasterID, B.*
FROM MasterTable A
JOIN ChildTable B
ON A.MasterID = B.MasterID
AND B.ChildID IN (SELECT Top 5 ChildID FROM ChildTable
WHERE MasterID = A.MasterID ORDER BY Whatever)

Related

How to get anchor details when using CTE?

I have a table with 2 columns (id, childId) with the following data:
1, 2
2, 4
3, 5
4, 6
5, null
6, null
I have the following CTE that gets the records and it works fine.
DECLARE #id TABLE (id int, childId int);
INSERT INTO #id SELECT 1, 2;
INSERT INTO #id SELECT 2, 4;
INSERT INTO #id SELECT 3, 5;
INSERT INTO #id SELECT 4, 6;
INSERT INTO #id SELECT 5, null;
INSERT INTO #id SELECT 6, null;
WITH cte AS
(
SELECT id, childId
FROM #id
WHERE id = 1
UNION ALL
SELECT b.id, b.childId
FROM #id b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT * FROM cte
However, I would like to add the anchor details so that the results will look like:
1, 2, null
2, 4, 1
4, 6, 1
6, null, 1
So that that 3rd column is the main anchor record.
Is this possible?
Just add a thrid column in CTE.
declare #mytable table ( id int, childId int );
insert #mytable
( id, childId )
values ( 1, 2 ),
( 2, 4 ),
( 3, 5 ),
( 4, 6 ),
( 5, null ),
( 6, null );
declare #id table ( id int );
insert into #id
select 1;
insert into #id
select 3;
with cte
as ( select id ,
childId ,
id root
from #mytable
where id in ( select id
from #id )
union all
select b.id ,
b.childId ,
cte.root
from #mytable b
inner join cte on b.id = cte.childId
)
select *
from cte;
WITH cte AS
(
SELECT id, childId, id AS anchorId
FROM mytable
WHERE
id IN (SELECT id FROM #id)
UNION ALL
SELECT b.id, b.childId, cte.anchorId
FROM mytable b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT
id
, childId
, case
WHEN id = anchorId THEN NULL
ELSE anchorId
END as anchorId
FROM cte
With test code provided:
DECLARE #id TABLE (id int, childId int);
INSERT INTO #id SELECT 1, 2;
INSERT INTO #id SELECT 2, 4;
INSERT INTO #id SELECT 3, 5;
INSERT INTO #id SELECT 4, 6;
INSERT INTO #id SELECT 5, null;
INSERT INTO #id SELECT 6, null;
WITH cte AS
(
SELECT id, childId, id AS anchorId
FROM #id
WHERE id IN (1,3)
UNION ALL
SELECT b.id, b.childId, cte.anchorId
FROM #id b
INNER JOIN cte
ON b.id = cte.childId
)
SELECT
id
, childId
, CASE
WHEN id = anchorId THEN NULL
ELSE anchorId
END AS anchorId
FROM cte

get all child from an parent id

hi i need a query to do this
my table data
ID ParentID DATA
--------------------------------
1 -1 a
2 1 b
3 2 c
4 3 d
5 3 f
and what ineed a query that take a ID as parameter and return all recursively childs and Itself
parameter : (ID=2)
return must be :
ID ParentID DATA
--------------------------------
2 1 b
3 2 c
4 3 d
5 3 f
Try this:
;with temp as (
select id, parentId, data from t
where id = 2
union all
select t.id, t.parentId, t.data from t
join temp on temp.id = t.parentId
)
select * from temp
Fiddle here.
This should do it for you:
create table #temp
(
id int,
parentid int,
data varchar(1)
)
insert #temp (id, parentid, data) values (1, -1, 'a')
insert #temp (id, parentid, data) values (2,1, 'b')
insert #temp (id, parentid, data) values (3,2, 'c')
insert #temp (id, parentid, data) values (4,3, 'd')
insert #temp (id, parentid, data) values (5,3, 'f')
; with cte as (
select id, parentid, data, id as topparent
from #temp
union all
select child.id, child.parentid, child.data, parent.topparent
from #temp child
join cte parent
on parent.id = child.parentid
)
select id, parentid, data
from cte
where topparent = 2
drop table #temp
EDIT or you can put the WHERE clause inside the first select
create table #temp
(
id int,
parentid int,
data varchar(1)
)
insert #temp (id, parentid, data) values (1, -1, 'a')
insert #temp (id, parentid, data) values (2,1, 'b')
insert #temp (id, parentid, data) values (3,2, 'c')
insert #temp (id, parentid, data) values (4,3, 'd')
insert #temp (id, parentid, data) values (5,3, 'f')
; with cte as (
select id, parentid, data, id as topparent
from #temp
WHERE id = 2
union all
select child.id, child.parentid, child.data, parent.topparent
from #temp child
join cte parent
on parent.id = child.parentid
)
select id, parentid, data
from cte
drop table #temp
Results:
id parentid data
2 1 b
3 2 c
4 3 d
5 3 f
declare #ID int = 2;
with C as
(
select ID, ParentID, DATA
from YourTable
where ID = #ID
union all
select T.ID, T.ParentID, T.DATA
from YourTable as T
inner join C
on T.ParentID = C.ID
)
select ID, ParentID, DATA
from C
Try on SE-Data
try this.
select * from table where id= 2 or parentid = 2

Sql Server2005 query problem

i have a table which contains the following fields
Supervisorid
Empid
This is just like a referral program. A guy can refer 3 guys under him i.e,
3 is referring three guys namely 4 5 8 similarly 4 is referring 9 10 and 11 likewise 8 is referring 12, 13 it goes like this..
I want a query to get the total no of down line members under a guy say 3
You can make use of Recursive CTE.
Something like this
DECLARE #Table TABLE(
Supervisorid INT,
Empid INT
)
INSERT INTO #Table SELECT 3, 4
INSERT INTO #Table SELECT 3, 5
INSERT INTO #Table SELECT 3, 8
INSERT INTO #Table SELECT 4, 9
INSERT INTO #Table SELECT 4, 10
INSERT INTO #Table SELECT 4, 11
INSERT INTO #Table SELECT 8, 12
INSERT INTO #Table SELECT 8, 13
DECLARE #ID INT
SELECT #ID = 3
;WITH Vals AS (
SELECT *
FROM #Table
WHERE SuperVisorID = #ID
UNION ALL
SELECT v.SuperVisorID,
t.Empid
FROM Vals v INNER JOIN
#Table t ON v.Empid = t.Supervisorid
)
SELECT SuperVisorID,
COUNT(Empid) Total
FROM Vals
GROUP BY SuperVisorID

"Distinct" column in SQL query

SELECT id, EmpNo
FROM EmployeesTable
EmpNo can be the same for 1 or more records in the results of the above query. I now want to add another column derived from EmpNo(lets call it EmpNo2) but only returning distinct values of EmpNo.
For example if the above query returns 100 records but there are 69 distinct EmpNo values and i modify the query to
SELECT id, EmpNo, Distinct EmpNo2
FROM EmployeesTable EmpNo
,
i want all the 100 rows to be returned but the last column EmpNo2 should return 69 distinct values of EmpNo field.
But as already know, using distinct in that way results into an error but i want to implement such functionality - and a subquery is not helping.
SAMPLE REQUIRED RESULTS
ID EmpNo EmpNo2
1 0T4/HR 0T4/HR
1 0T4/HR 2VP/E
1 0T4/HR xT9/67
1 0T4/HR
1 0T4/HR
2 2VP/E
2 2VP/E
2 2VP/E
2 2VP/E
2 2VP/E
3 XT9/67
3 XT9/67
3 xT9/67
3 XT9/67
How about:
Select id, empno, empno2
from employeestable left outer join (
SELECT min([id]) as minid
,[empno] empno2
FROM [EmployeesTable]
group by empno) etab2 on employeestable.id = etab2.minid
You're saying a subquery won't work, though - why not?
Your requirement is not clear and I also have very little information. Following is what you need. This can be even better but it is just a try.
declare #temp table
(
uniqueid int identity(1, 1),
id int,
empno varchar(50),
empno2 varchar(50)
)
insert into #temp select 1, '0T4/HR', null
insert into #temp select 1, '0T4/HR' , null
insert into #temp select 1 , '0T4/HR' , null
insert into #temp select 1, '0T4/HR' , null
insert into #temp select 1, '0T4/HR' , null
insert into #temp select 2, '2VP/E' , null
insert into #temp select 2, '2VP/E' , null
insert into #temp select 2, '2VP/E' , null
insert into #temp select 2, '2VP/E' , null
insert into #temp select 2, '2VP/E' , null
insert into #temp select 3, 'XT9/67' , null
insert into #temp select 3, 'XT9/67' , null
insert into #temp select 3, 'xT9/67' , null
insert into #temp select 3, 'XT9/67' , null
SELECT ROW_NUMBER() OVER (ORDER BY id) AS id, empno into #temp FROM #temp group by empno, id
update #temp set empno2 = t2.empno
from #temp t inner join #temp t2 on t.uniqueid = t2.id
select * from #temp
drop table #temp

t-sql recursive query

Based on an existing table I used CTE recursive query to come up with following data. But failing to apply it a level further.
Data is as below
id name parentid
--------------------------
1 project 0
2 structure 1
3 path_1 2
4 path_2 2
5 path_3 2
6 path_4 3
7 path_5 4
8 path_6 5
I want to recursively form full paths from the above data. Means the recursion will give the following output.
FullPaths
-------------
Project
Project\Structure
Project\Structure\Path_1
Project\Structure\Path_2
Project\Structure\Path_3
Project\Structure\Path_1\path_4
Project\Structure\Path_2\path_5
Project\Structure\Path_3\path_6
Thanks
Here's an example CTE to do that:
declare #t table (id int, name varchar(max), parentid int)
insert into #t select 1, 'project' , 0
union all select 2, 'structure' , 1
union all select 3, 'path_1' , 2
union all select 4, 'path_2' , 2
union all select 5, 'path_3' , 2
union all select 6, 'path_4' , 3
union all select 7, 'path_5' , 4
union all select 8, 'path_6' , 5
; with CteAlias as (
select id, name, parentid
from #t t
where t.parentid = 0
union all
select t.id, parent.name + '\' + t.name, t.parentid
from #t t
inner join CteAlias parent on t.parentid = parent.id
)
select *
from CteAlias
Try something like this:
WITH Recursive AS
(
SELECT
ID,
CAST(PathName AS VARCHAR(500)) AS 'FullPaths',
1 AS 'Level'
FROM
dbo.YourTable
WHERE
ParentID = 0
UNION ALL
SELECT
tbl.ID,
CAST(r.FullPaths + '\' + tbl.PathName AS VARCHAR(500)) AS 'FullPaths',
r.Level + 1 AS 'Level'
FROM
dbo.YourTable tbl
INNER JOIN
Recursive r ON tbl.ParentID = r.ID
)
SELECT * FROM Recursive
ORDER BY Level, ID
Output:
ID FullPaths Level
1 project 1
2 project\structure 2
3 project\structure\path_1 3
4 project\structure\path_2 3
5 project\structure\path_3 3
6 project\structure\path_1\path_4 4
7 project\structure\path_2\path_5 4
8 project\structure\path_3\path_6 4
try this:
DECLARE #YourTable table (id int, nameof varchar(25), parentid int)
INSERT #YourTable VALUES (1,'project',0)
INSERT #YourTable VALUES (2,'structure',1)
INSERT #YourTable VALUES (3,'path_1',2)
INSERT #YourTable VALUES (4,'path_2',2)
INSERT #YourTable VALUES (5,'path_3',2)
INSERT #YourTable VALUES (6,'path_4',3)
INSERT #YourTable VALUES (7,'path_5',4)
INSERT #YourTable VALUES (8,'path_6',5)
;WITH Rec AS
(
SELECT
CONVERT(varchar(max),nameof) as nameof,id
FROM #YourTable
WHERE parentid=0
UNION ALL
SELECT
CONVERT(varchar(max),r.nameof+'\'+y.nameof), y.id
FROM #yourTable y
INNER jOIN Rec r ON y.parentid=r.id
)
select * from rec
output:
nameof
-----------------------------------------------
project
project\structure
project\structure\path_1
project\structure\path_2
project\structure\path_3
project\structure\path_3\path_6
project\structure\path_2\path_5
project\structure\path_1\path_4
(8 row(s) affected)
Something like
;WITH MyCTE AS
(
SELECT
name AS FullPaths, id
FROM
MyTable
WHERE
parentid = 0 /*Normally it'd be IS NULL with an FK linking the 2 columns*/
UNION ALL
SELECT
C.FullPaths + '\' + M.name, M.id
FROM
MyCTE C
JOIN
MyTable M ON M.parentid = C.id
)
SELECT FullPaths FROM MyCTE
You'll have to change the name of #test table I was using.
WITH cte(id, name, parentid) AS
(
SELECT id, convert(varchar(128), name), parentid
FROM #test
WHERE parentid = 0
UNION ALL
SELECT t.id, convert(varchar(128), c.name +'\'+t.name), t.parentid
FROM #test t
INNER JOIN cte c
ON c.id = t.parentid
)
SELECT name as FullPaths
FROM cte
order by id