Having data with Self Referencing Relationship - tsql

I have Tree like this
Folder0
|
|__Folder1
| |
| |_______Folder2
| | |_______Folder3
| | |_______Folder4
| |_______Folder5
| |_______Folder6
|__Folder7
Fields of the table are like this Id, fld_Id, Name, which fld_Id is the parent Id of the folder
Id , fld_Id , Name
________________________________
1 Null Folder0
2 Null Folder7
3 1 Folder1
4 3 Folder2
5 4 Folder3
6 4 Folder4
7 3 Folder5
7 3 Folder6
how can I get the List of the all directories which are located under the Folder1
which would be
Id , fld_Id , Name
________________________________
3 1 Folder1
4 3 Folder2
5 4 Folder3
6 4 Folder4
7 3 Folder5
7 3 Folder6

Try using recursive CTE like below :
;with CTE as
(
SELECT Id,fld_Id,Name FROM Test t1 WHERE Id = 3
UNION ALL
SELECT t1.Id, t1.fld_Id, t1.Name
FROM Test t1
INNER JOIN CTE ON t1.fld_Id = CTE.Id
)
SELECT * FROM CTE order by Id
SQL Fiddle Demo

Related

Get different LIMIT on each group on postgresql rank

To get 2 rows from each group I can use ROW_NUMBER() with condition <= 2 at last but my question is what If I want to get different limits on each group e.g 3 rows for section_id 1, 1 rows for 2 and 1 rows for 3?
Given the following table:
db=# SELECT * FROM xxx;
id | section_id | name
----+------------+------
1 | 1 | A
2 | 1 | B
3 | 1 | C
4 | 1 | D
5 | 2 | E
6 | 2 | F
7 | 3 | G
8 | 2 | H
(8 rows)
I get the first 2 rows (ordered by name) for each section_id, i.e. a result similar to:
id | section_id | name
----+------------+------
1 | 1 | A
2 | 1 | B
5 | 2 | E
6 | 2 | F
7 | 3 | G
(5 rows)
Current Query:
SELECT
*
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY section_id ORDER BY name) AS r,
t.*
FROM
xxx t) x
WHERE
x.r <= 2;
Create a table to contain the section limits, then join. The big advantage being that as new sections are required or limits change maintenance is reduced to a single table update and comes at very little cost. See example.
select s.section_id, s.name
from (select section_id, name
, row_number() over (partition by section_id order by name) rn
from sections
) s
left join section_limits sl on (sl.section_id = s.section_id)
where
s.rn <= coalesce(sl.limit_to,2);
Just fix up your where clause:
with numbered as (
select row_number() over (partition by section_id
order by name) as r,
t.*
from xxx t
)
select *
from numbered
where (section_id = 1 and r <= 3)
or (section_id = 2 and r <= 1)
or (section_id = 3 and r <= 1);

How does one print depth-level in a Postgres query that uses RECURSIVE to select descendants?

I have a table persons that contains a column for parent_id, which refers to another row in the same table. Assume this is the logical hierarchy:
P1
P2 P3 P4
P5 P6 P7 P8 P9 P10
I have written a query that prints all parents of a given node, along with the height above the node, and it seems to work fine:
WITH
RECURSIVE ancestors AS (
SELECT id, parent_id
FROM persons
WHERE id = 8
UNION
SELECT p.id, p.parent_id
FROM persons p
INNER JOIN ancestors
ON
p.id = ancestors.parent_id
)
SELECT persons.id, persons.name,
ROW_NUMBER() over () as height
FROM ancestors
INNER JOIN persons
ON
ancestors.id = persons.id
WHERE
persons.id <> 8
Result:
id | name | height
-------+-------------+---------
3 | P3 | 1
1 | P1 | 2
(2 rows)
I now want to write a query that similarly prints all descendants, along with depth. Here's the query so far (same as above with id and parent_id swapped in the UNION join):
WITH
RECURSIVE descendants AS (
SELECT id, parent_id
FROM persons
WHERE id = 1
UNION
SELECT p.id, p.parent_id
FROM persons p
INNER JOIN descendants
ON
p.parent_id = descendants.id
)
SELECT persons.id, persons.name,
ROW_NUMBER() over () as depth
FROM descendants
INNER JOIN persons
ON
descendants.id = persons.id
WHERE
persons.id <> 1
This gives the following result:
id | name | depth
-------+-------------+---------
2 | P2 | 1
3 | P3 | 2
4 | P4 | 3
5 | P5 | 4
6 | P6 | 5
7 | P7 | 6
8 | P8 | 7
9 | P9 | 8
10 | P10 | 9
(9 rows)
Clearly, the depth is all wrong. ROW_NUMBER() isn't doing what I want. How do I go about this?
I've thought about using a counter within the recursive part of the query itself, which increments every time it is run, but I'm not sure if there's a way to achieve that.
Use an additional integer column with values incremented at each recursive step.
WITH RECURSIVE descendants AS (
SELECT id, parent_id, 0 AS depth
FROM persons
WHERE id = 1
UNION
SELECT p.id, p.parent_id, d.depth+ 1
FROM persons p
INNER JOIN descendants d
ON p.parent_id = d.id
)
SELECT p.id, p.name, depth
FROM descendants d
INNER JOIN persons p
ON d.id = p.id
WHERE p.id <> 1;
id | name | depth
----+------+-------
2 | P2 | 1
3 | P3 | 1
4 | P4 | 1
5 | P5 | 2
6 | P6 | 2
7 | P7 | 2
8 | P8 | 2
9 | P9 | 2
10 | P10 | 2
(9 rows)
Db<>fiddle.

inserting new recording and updating the foreignkey

I have tables like this
MainTable
Id FolderName Description UserId
1 Folder1 Description1 1
2 Folder1 Description2 1
3 Folder1 Desctiption3 1
4 Folder2 Description1 2
5 Folder2 Description2 2
ChildTable
Id FolderId Title UserId imageName
1 1 AA 1 AAA
2 1 BB 1 BBB
3 2 CC 1 CCC
4 3 DD 1 DDD
5 4 EE 2 EEE
I want to select all records in MainTable and ChildTable For userId=1 and insert them again into MainTable/ChildTable but with UserId=5, so the FolderId for the new records should be updated, so the result should be like this:
Id FolderName Description UserId
1 Folder1 Description1 1
2 Folder1 Description2 1
3 Folder1 Desctiption3 1
4 Folder2 Description1 2
5 Folder2 Description2 2
6 Folder1 Description1 5
7 Folder1 Description2 5
8 Folder1 Desctiption3 5
Id FolderId Title UserId imageName
1 1 AA 1 AAA
2 1 BB 1 BBB
3 2 CC 1 CCC
4 3 DD 1 DDD
5 4 EE 2 EEE
6 6 AA 5 AAA
7 6 BB 5 BBB
8 7 CC 5 CCC
9 8 DD 5 DDD
how can i do that?
thank you
Try this:
DECLARE #t table (OldFolderId int, NewFolderId int);
MERGE MainTable AS m
USING (SELECT Id, FolderName, Description
FROM MainTable AS m
WHERE UserId = 1) AS src
ON 1=2
WHEN NOT MATCHED THEN
INSERT (FolderName, Description, UserId)
VALUES (src.FolderName, src.Description, 5)
OUTPUT src.Id AS OldFolderId, inserted.Id AS NewFolderId
INTO #t;
INSERT ChildTable (FolderId, Title, UserId, imageName)
SELECT t.NewFolderId, Title, 5, imageName
FROM ChildTable c
INNER JOIN #t t ON t.OldFolderId = c.FolderID
WHERE c.UserId = 1;
SQLFiddle here

Find missing entries in link table

I use SQL-Server 2008 and have the following tables.
users
userid
1
2
3
objects
objectid | category
9 | A
8 | B
7 | A
6 | C
userobjects
userid | objectid
1 | 9
3 | 7
3 | 6
As you can see, userobjects is a link table. Unfortunately it is missing some entries. I could include them with a script but I wonder if there is a solution in sql.
For every userid and every objectid that belongs to category 'A' there should be an entry in userobjects. So what I wanted to have is this:
userid | objectid
1 | 9
1 | 7
2 | 9
2 | 7
3 | 9
3 | 7
3 | 6
I could include them with a script but I wonder if there is a solution
in sql.
This is a select query using UNION (here is SQL-DEMO);
select u.userId, o.objectId
from objects o cross join users u
where o.category = 'A'
union
select u.userId, o.objectId
from users u join userobjects uj on u.userid = uj.userId
join objects o on uj.objectid = o.objectid
where o.category <> 'A'
order by u.userid,o.objectid desc
--RESULTS
userId objectId
1 9
1 7
2 9
2 7
3 9
3 7
3 6
You can do it without a UNION using a LEFT join and a non-traditional INNER JOIN to objects
SELECT DISTINCT u.userid,
o.objectid
FROM users u
LEFT JOIN userobjects uj
ON u.userid = uj.userid
INNER JOIN objects o
ON uj.objectid = o.objectid
OR ( o.category = 'A' )
ORDER BY u.userid,
o.objectid DESC
Fiddle

nber of rows within a group in oracle

In order to generate a report in ireport i need this query in oracle 10g.
SCHOOL:
SELECT STID,NAME,DEPT,SUM(CHARGE)
STID | PROG | DEPT | CHARGE
1 1 A 1
2 1 B 2
3 2 A 2
4 2 B 1
5 1 A 2
Desired OUTPUT:
DEPT | PROG | NBER_OF_STID | TOT_CHG
A 1 2 3
2 1 2
B 1 1 2
2 1 1
this is my query
SELECT DISTINCT DEPT, DISTINCT PROG, COUNT(STID), SUM (CHARGE) TOT_CHG
FROM SCHOOL
GROUP BY DEPT, PROG, STID, CHARGE
Help Thanks.
You need to group by only the columns that aren't going to be aggregated.
Try this:
SELECT DEPT, PROG, COUNT(STID) NBER_OF_STID, SUM (CHARGE) TOT_CHG
FROM SCHOOL
GROUP BY DEPT, PROG
Note: in your query you'll always get a tabular view, so results will be like this:
DEPT | PROG | NBER_OF_STID | TOT_CHG
A 1 2 3
A 2 1 2
B 1 1 2
B 2 1 1
IMHO, the visual formatting should be made in the report itself (ireport)