Consolidate rows in one table based on duplicates in another table - sql-server

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

Related

Query optimization for oracle database having foreign key to the same table

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;

Rank or merge sequential rows

I have a log file I need to either rank (but treating sequential and equal rows as ties), or merge sequential equal rows (based on specific column). My table looks like below, The Start and Stop are all being sequential (within the same ID window)
ID Start Stop Value
1 0 1 A
1 1 2 A
1 2 3 A
1 3 4 B
1 4 5 B
1 5 6 A
2 3 4 A
I have two approches to get what I need.
Approach 1: Rank (treating sequential rows with equal values in "Value" as ties) and using ID as partition.
This should give the output below. But how do I do the special rank: Treating sequential rows with equal values in "Value" as ties.
Select *,
rank() OVER (partition by id order by start, stop) as Rank,
XXX as SpecialRank
from Table
ID Start Stop Value Rank SpecialRank
1 0 1 A 1 1
1 1 2 A 2 1
1 2 3 A 3 1
1 3 4 B 4 2
1 4 5 B 5 2
1 5 6 A 6 3
2 3 4 A 1 1
Approach 2: Merge sequential rows with equal values in "Value".
This will shall create a table like below.
ID Start Stop Value
1 0 3 A
1 3 5 B
1 5 6 A
2 3 4 A
I don't know if this helps, but I have also a nextValue column that might help in this
ID Start Stop Value NextValue
1 0 1 A A
1 1 2 A A
1 2 3 A B
1 3 4 B B
1 4 5 B A
1 5 6 A A
2 3 4 A ...
Example-table:
CREATE TABLE #Table ( id int, start int, stop int, Value char(1), NextValue char(1));
INSERT INTO #Table values (1,0, 1, 'A', 'A');
INSERT INTO #Table values (1,1, 2, 'A', 'A');
INSERT INTO #Table values (1,2, 3, 'A', 'B');
INSERT INTO #Table values (1,3, 4, 'B', 'B');
INSERT INTO #Table values (1,4, 5, 'B', 'A');
INSERT INTO #Table values (1,5, 6, 'A', 'A');
INSERT INTO #Table values (2,3, 4, 'A', null);
Use a self join to an aggregate subquery from the full set, e.g.
with rankTable (id, value) as
( select 1, 'A' union all select 1, 'A' union all select 1, 'B' union all select 2, 'A')
select t2.* from rankTable t1 join (
select id, value, rank() over (partition by id order by value) as specialRank from
(
select distinct id, value
from rankTable
) t) t2 on t2.id =t1.id and t2.value = t1.value
id value specialRank
1 A 1
1 A 1
1 B 2
2 A 1

Search child rows for values

I have something like this:
Transaction Customer
1 Cust1
2 Cust2
3 Cust3
4 Cust4
TransID Code
2 A
2 B
2 D
3 A
4 B
4 C
If I want to be able to do something like "IF Customer 'Cust1' Has code 'A'", how should I best build a view? I want to end up being able to query something like "Select Customer from View where Code in [Some list of codes]" OR "Select Cust1 from View Having Codes in [Some list of codes]"
While I can do something like
Customer | Codes
Cust1 | A, B, D
Etc.
SELECT Transaction from Tbl where Codes like 'A'
This seems to me to be an impractical way to do it.
Here's how I'd do it
;with xact_cust (xact, cust) as
(
select 1, 'cust1' union all
select 2, 'cust2' union all
select 3, 'cust3' union all
select 4, 'cust4'
), xact_code (xact, code) as
(
select 2, 'A' union all
select 2, 'B' union all
select 2, 'D' union all
select 3, 'A' union all
select 4, 'B' union all
select 4, 'C'
)
select Cust, Code
from xact_cust cust
inner join xact_code code
on cust.xact = code.xact
where exists (select 1
from xact_code i
where i.xact = code.xact
and i.code = 'A')
If you NEED the codes serialized into a delimited list, take a look at this article: What this query does to create comma delimited list SQL Server?
Here's another option...
IF OBJECT_ID('tempdb..#CustomerTransaction', 'U') IS NOT NULL
DROP TABLE #CustomerTransaction;
CREATE TABLE #CustomerTransaction (
TransactionID INT NOT NULL PRIMARY KEY,
Customer CHAR(5) NOT NULL
);
INSERT #CustomerTransaction (TransactionID, Customer) VALUES
(1, 'Cust1'), (2, 'Cust2'), (3, 'Cust3'),
(4, 'Cust4'), (5, 'Cust5');
IF OBJECT_ID('tempdb..#TransactionCode', 'U') IS NOT NULL
DROP TABLE #TransactionCode;
CREATE TABLE #TransactionCode (
TransactionID INT NOT NULL,
Code CHAR(1) NOT NULL
);
INSERT #TransactionCode (TransactionID, Code) VALUES
(2, 'A'), (2, 'B'), (2, 'D'), (3, 'A'), (4, 'B'), (4, 'C');
--SELECT * FROM #CustomerTransaction ct;
--SELECT * FROM #TransactionCode tc;
--=============================================================
SELECT
ct.TransactionID,
ct.Customer,
CodeList = STUFF(tcx.CodeList, 1, 1, '')
FROM
#CustomerTransaction ct
CROSS APPLY (
SELECT
', ' + tc.Code
FROM
#TransactionCode tc
WHERE
ct.TransactionID = tc.TransactionID
ORDER BY
tc.Code ASC
FOR XML PATH('')
) tcx (CodeList);
Results...
TransactionID Customer CodeList
------------- -------- -----------
1 Cust1 NULL
2 Cust2 A, B, D
3 Cust3 A
4 Cust4 B, C
5 Cust5 NULL

SQL JOIN on list of IDs in a column

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

Search within ColA duplicates against specific unique vals in ColB to exclude all of ColA

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)

Resources