I've got a multi-tiered object in my database called MyFolder. MyFolder can be a child of another MyFolder at infinite levels. The table is defined as follows:
CREATE TABLE dbo.MyFolders
(
MyFolderId INT IDENTITY(1,1) NOT NULL,
ParentMyFolderId INT NULL,
Name NVARCHAR(50) NOT NULL,
Depth INT NOT NULL,
Ancestry NVARCHAR(max) NOT NULL,
CONSTRAINT PK_MyFolders PRIMARY KEY CLUSTERED (MyFolderId ASC),
CONSTRAINT FK_MyFolders_MyFolders FOREIGN KEY(ParentMyFolderId) REFERENCES dbo.MyFolders (MyFolderId)
)
It has data like:
MyFolderId ParentMyFolderId Name Depth Ancestry
1 NULL Folder1 0 /
2 1 Folder1A 1 /1/
3 1 Folder1B 1 /1/
4 1 Folder1C 1 /1/
5 4 Folder1C1 2 /1/4/
6 4 Folder1C2 2 /1/4/
7 6 Folder1C2a 3 /1/4/6/
8 6 Folder1C2b 3 /1/4/6/
This works quite well for everything needed in my system. However, it gets tricky if I want to retrieve a query like the following:
MyFolderId Name
1 Folder1
2 Folder1/Folder1A
3 Folder1/Folder1B
4 Folder1/Folder1C
5 Folder1/Folder1C/Folder1C1
6 Folder1/Folder1C/Folder1C2
7 Folder1/Folder1C/Folder1C2/Folder1C2a
8 Folder1/Folder1C/Folder1C2/Folder1C2b
Is there a way to JOIN on the ancestry field in order to get the ancestor names? Or another way using the ParentMyFolderId column? I do have a table-valued split string function called SplitString(value, delimiter).
This can be done using recursive queries, just append your current folder name to what you had previously.
Query:
;WITH Source (MyFolderId, ParentMyFolderId, Name, Depth, Ancestry)
AS (
SELECT 1, NULL, 'Folder1', 0, '/'
UNION ALL
SELECT 2, 1, 'Folder1A', 1, '/1/'
UNION ALL
SELECT 3, 1, 'Folder1B', 1, '/1/'
UNION ALL
SELECT 4, 1, 'Folder1C', 1, '/1/'
UNION ALL
SELECT 5, 4, 'Folder1C1', 2, '/1/4/'
UNION ALL
SELECT 6, 4, 'Folder1C2', 2, '/1/4/'
UNION ALL
SELECT 7, 6, 'Folder1C2a', 3, '/1/4/6/'
UNION ALL
SELECT 8, 6, 'Folder1C2b', 3, '/1/4/6/'
),
cte AS
(
SELECT S.MyFolderID, S.ParentMyFolderId, CAST(S.Name AS VARCHAR(MAX)) AS Name
FROM Source AS S
WHERE ParentMyFolderId IS NULL
UNION ALL
SELECT S.MyFolderID, S.ParentMyFolderId, C.Name + '/' + S.Name
FROM Source AS S
INNER JOIN cte AS C
ON C.MyFolderId = S.ParentMyFolderId
)
SELECT *
FROM cte
Here's a recursive CTE as mentioned in the comments:
WITH TreeStructure(MyFolderId, Name) AS
(
SELECT MyFolderId, CONVERT(varchar(500), Name)
FROM MyFolders WHERE ParentMyFolderId IS NULL
UNION ALL
SELECT sd.MyFolderId, CONVERT(varchar(500), t.Name + '/' + sd.Name)
FROM MyFolders sd
JOIN TreeStructure t ON sd.ParentMyFolderId = t.MyFolderId
WHERE sd.ParentMyFolderId IS NOT NULL
)
SELECT * FROM TreeStructure
Results:
MyFolderId Name
----------- ----------------------------------------
1 Folder1
2 Folder1/Folder1A
3 Folder1/Folder1B
4 Folder1/Folder1C
5 Folder1/Folder1C/Folder1C1
6 Folder1/Folder1C/Folder1C2
7 Folder1/Folder1C/Folder1C2/Folder1C2a
8 Folder1/Folder1C/Folder1C2/Folder1C2b
Related
This question already has answers here:
Get top 1 row of each group
(19 answers)
Closed 11 months ago.
I have a table that looks like this.
Category
Type
fromDate
Value
1
1
1/1/2022
5
1
2
1/1/2022
10
2
1
1/1/2022
7.5
2
2
1/1/2022
15
3
1
1/1/2022
3.5
3
2
1/1/2022
5
3
1
4/1/2022
5
3
2
4/1/2022
10
I'm trying to filter this table down to filter down and keep the most recent grouping of Category/Type. IE rows 5 and 6 would be removed in the query since they are older records.
So far I have the below query but I am getting an aggregate error due to not aggregating the "Value" column. My question is how do I get around this without aggregating? I want to keep the actual value that is in the column.
SELECT T1.Category, T1.Type, T2.maxDate, T1.Value
FROM (SELECT Category, Type, MAX(fromDate) AS maxDate
FROM Table GROUP BY Category,Type) T2
INNER JOIN Table T1 ON T1.Category=T2.Category
GROUP BY T1.Category, T1.Type, T2.MaxDate
This has been asked and answered dozens and dozens of times. But it was quick and painless to type up an answer. This should work for you.
declare #MyTable table
(
Category int
, Type int
, fromDate date
, Value decimal(5,2)
)
insert #MyTable
select 1, 1, '1/1/2022', 5 union all
select 1, 2, '1/1/2022', 10 union all
select 2, 1, '1/1/2022', 7.5 union all
select 2, 2, '1/1/2022', 15 union all
select 3, 1, '1/1/2022', 3.5 union all
select 3, 2, '1/1/2022', 5 union all
select 3, 1, '4/1/2022', 5 union all
select 3, 2, '4/1/2022', 10
select Category
, Type
, fromDate
, Value
from
(
select *
, RowNum = ROW_NUMBER() over(partition by Category, Type order by fromDate desc)
from #MyTable
) x
where x.RowNum = 1
order by x.Category
, x.Type
Here is my table structure
Here is my query. please suggest a better way to do this query in the oracle database for the output given below?
select * from UMS_DEV_MGMT_GRP where grp_typ_id=1
union
select * from UMS_DEV_MGMT_GRP where par_grp_id in
(select grp_id from UMS_DEV_MGMT_GRP where grp_typ_id=1);
The way you put it, have a look at
sample data:
SQL> with ums_dev_mgmt_grp (grp_id, grp_nm, par_grp_id, grP_typ_id) as
2 (select 1, 'home' , null, 1 from dual union all
3 select 2, 'test1', 1 , 7 from dual union all
4 select 3, 'test2', 1 , 7 from dual union all
5 select 4, 'test3', null, 1 from dual union all
6 select 5, 'test4', null, 2 from dual union all
7 select 6, 'test5', 5 , 7 from dual
8 )
query begins here:
9 select *
10 from ums_dev_mgmt_grp
11 where nvl(par_grp_id, grp_typ_id) = 1;
GRP_ID GRP_N PAR_GRP_ID GRP_TYP_ID
---------- ----- ---------- ----------
1 home 1
2 test1 1 7
3 test2 1 7
4 test3 1
SQL>
Not sure exactly what the intent is, but the following SQL should result in the required table result.
SELECT * FROM UMS_DEV_MGMT_GRP WHERE grp_typ_id = 1 OR par_grp_id = 1;
I have one table table1 that might look like this, where there could be duplicates on 2 fields Cnumber and Dob and then unique pkSID:
pkSID Cnumber Dob
1 12345 01/02/2002
2 12345 01/02/2002
3 12345 01/02/2002
4 12345 01/02/2002
5 12345 01/02/2002
There can be multiple occurrences of this in table1. I then have another table that references the pkSID, and I want to consolidate those rows so they all only reference one of the pkSID in table1, so table2 will look like this initially:
pkSTID fkSID OtherVal1 OtherVal2
1 1 s x
2 2 t f
3 3 a d
4 4 v g
5 5 b z
And then after the consolidation:
pkSTID fkSID OtherVal1 OtherVal2
1 1 s x
2 1 t f
3 1 a d
4 1 v g
5 1 b z
How can I find those rows in table1 and then consolidate in table2?
Try this:
Note: I'm considering pkSID is in continuation for relative Cnumber and Dob
CREATE TABLE #TABLE1(pkSID INT,Cnumber INT,Dob DATE)
INSERT INTO #TABLE1
SELECT 1, 12345, '01/02/2002' UNION ALL
SELECT 2, 12345, '01/02/2002' UNION ALL
SELECT 3, 12345, '01/02/2002' UNION ALL
SELECT 4, 12345, '01/02/2002' UNION ALL
SELECT 5, 12345, '01/02/2002'
CREATE TABLE #TABLE2(pkSTID INT,fkSID INT,OtherVal1 VARCHAR(10),OtherVal2 VARCHAR(10))
INSERT INTO #TABLE2
SELECT 1, 1, 's', 'x' UNION ALL
SELECT 2, 2, 't', 'f' UNION ALL
SELECT 3, 3, 'a', 'd' UNION ALL
SELECT 4, 4, 'v', 'g' UNION ALL
SELECT 5, 5, 'b', 'z'
SELECT T2.pkSTID, T3.min_pkSID fkSID, T2.OtherVal1 , T2.OtherVal2
FROM #TABLE2 T2
INNER JOIN
(
SELECT MIN(T1.pkSID) min_pkSID, MAX(T1.pkSID) max_pkSID FROM #TABLE1 T1 GROUP BY T1.Cnumber, T1.Dob --T1 ON T2.fkSID = T1.pkSID
)T3
ON T2.fkSID BETWEEN T3.min_pkSID AND T3.max_pkSID
I apologize in advance I feel like I'm missing something really stupid simple. (and let's ignore database structure as I'm kind of locked into that).
I have, let's use customer orders - an order number can be shipped to more than one place. For the sake of ease I'm just illustrating three but it could be more than that (home, office, gift, gift2, gift 3, etc)
So my table is:
Customer orders:
OrderID MailingID
--------------------
1 1
1 2
1 3
2 1
3 1
3 3
4 1
4 2
4 3
What I need to find is OrderIDs that have been shipped to MailingID 1 but not 2 (basically what I need to find is orderID 2 and 3 above).
If it matters, I'm using Sql Express 2012.
Thanks
Maybe this could help:
create table #temp(
orderID int,
mailingID int
)
insert into #temp
select 1, 1 union all
select 1, 2 union all
select 1, 3 union all
select 2, 1 union all
select 3, 1 union all
select 3, 3 union all
select 4, 1 union all
select 4, 2 union all
select 4, 3
-- find orderIDs that have been shipeed to mailingID = 1
select
distinct orderID
from #temp
where mailingID = 1
except
-- find orderIDs that have been shipeed to mailingID = 2
select
orderID
from #temp
where mailingID = 2
drop table #temp
A simple Subquery With NOT IN Operator should work.
SELECT DISTINCT OrderID
FROM <tablename> a
WHERE orderid NOT IN (SELECT orderid
FROM <tablename> b
WHERE b.mailingID = 2)
I am a beginner at SQL Server and I have a question about how best to do this.
I have a table that looks like this:
ID Parent Level
1 NULL 0
2 1 1
3 1 1
4 2 2
5 2 2
6 3 2
7 2 2
8 5 4
9 4 3
10 6 3
11 6 3
As you can see, all the entries have a Parent and a Level and the database is organized in a tree structure. There are some entries where the Level is set incorrectly such as entry ID #8. The Parent of 8 is 5 and ID 5 has a level of 2 so the level of 8 should be 3 and not 4. There are many incorrect Level values in my table and I'm not sure how to fix this. So far I have this:
UPDATE myTable
SET level=level-1
FROM myTable
WHERE ???;
I am not sure how to fill in the WHERE part or whether this is the best way to do this. Any suggestions are gladly appreciated.
This will show you the rows that have issues.
select
a.id,
a.level,
b.level as parentlevel
from
tablename a
join tablename b on a.parent = b.id
where
a.level <> b.level+1
If you are using SQL Server 2005 or SQL Server 2008, then you can use a recursive CTE (common table expression). The books online article is pretty straight forward, but here's how you can do it with your code.
-- Create temporary table and insert values
CREATE TABLE dbo.ctetest (childid int primary key not null, parentid int null, level int null);
INSERT INTO dbo.ctetest (childid, parentid) SELECT 1, NULL;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 2, 1;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 3, 1;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 4, 2;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 5, 2;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 6, 3;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 7, 2;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 8, 5;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 9, 4;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 10, 6;
INSERT INTO dbo.ctetest (childid, parentid) SELECT 11, 6;
-- Update table with level data from recursive CTE
WITH recursivecte (childid, parentid, level)
AS
(SELECT childid
, parentid
, 'level' = 0
FROM dbo.ctetest
WHERE parentid IS NULL
UNION ALL
SELECT ct.childid
, ct.parentid
, 'level' = rc.level + 1
FROM dbo.ctetest ct
JOIN recursivecte rc
ON ct.parentid = rc.childid)
UPDATE ct
SET level = rc.level
FROM dbo.ctetest ct
JOIN recursivecte rc
ON ct.childid = rc.childid;
-- Verify results
SELECT *
FROM dbo.ctetest;
Here's the output from the above query:
Child ID Parent ID Level
1 NULL 0
2 1 1
3 1 1
4 2 2
5 2 2
6 3 2
7 2 2
8 5 3
9 4 3
10 6 3
11 6 3
Please note I tested the above code using SQL Server 2008. I'm assuming it will work in SQL Server 2005 since CTE's were introduced in 2005.