I need to find multiple rows with iteration without using loop - sql-server

Let's say I have 2 tables.
Users Table
and Have one more table which defines hierarchy of user.
hierarchy Table
So as you can see:
C is a supervisor of D
B is a supervisor of C
A is a supervisor of B
So when I pass User D, then it should return all the supervisor like A,B,C
same when I pass User C, then it should return all the supervisor like A,B
What I tried.
Create table Users
(
Id int primary key identity (1,1),
Name varchar(1),
)
Insert into Users values ('A')
Insert into Users values ('B')
Insert into Users values ('C')
Insert into Users values ('D')
Create table Hierarchy
(
Id int primary key identity (1,1),
EmployeeId int FOREIGN KEY REFERENCES Users(Id),
SupervisorId int FOREIGN KEY REFERENCES Users(Id)
)
Insert into Hierarchy values (4,3)
Insert into Hierarchy values (3,2)
Insert into Hierarchy values (2,1)
select * from Users
select * from Hierarchy
with HierarchyData as
(
select mbh.* from Hierarchy mbh where mbh.EmployeeId = 4
union all
select mbh.* from Hierarchy mbh
join Hierarchy on mbh.SupervisorId = Hierarchy.EmployeeId
where mbh.EmployeeId <> 4
)
select e.Name as EmpName, s.Name as SupervisorName from HierarchyData h
join Users e on h.EmployeeId = e.Id
join Users s on h.SupervisorId = s.Id
But I am getting only one level data.
Any kind of help would be appreciated.

#Vishal as per my understanding written query for you can you please check it's working or not?
here I used LEFT JOIN you can go with INNER JOIN
If you go with LEFT JOIN as per your example A not have any supervisor so the record can be empty.
If you go with INNER JOIN as per your example you got only the B, C, D record.
Please check the below test query.
DECLARE #User TABLE
(
UserID INT,
UserName NVARCHAR(50)
)
DECLARE #EmployeeTable TABLE
(
ID INT,
EmployeeID INT,
supervisorID INT
)
INSERT INTO #User VALUES(1,'A'),
(2,'B'),
(3,'C'),
(4,'D')
INSERT INTO #EmployeeTable VALUES
(1,4,3),
(2,3,2),
(3,2,1)
SELECT [U].[UserName] [EmployeeName],
[ET].[EmployeeID],
[ET].[SupervisorID],[ST].[SupervisorName]
FROM #User [U]
LEFT JOIN #EmployeeTable [ET]
ON [U].[UserID] = [ET].[EmployeeID]
LEFT JOIN
(
SELECT [U].UserName [SupervisorName] ,[ST].* FROM #User [U]
INNER JOIN #EmployeeTable [ST]
ON [ST].[supervisorID] = [U].[UserID]
) [ST]
ON [ST].[supervisorID] = [ET].[supervisorID]
Left join query result
Inner join query result
let me know if I can help more :).

Related

More efficient way to write this query - several M:M relationships

I am using SQL Server 2014 SP3.
I have the following hypothetical database structure.
There are accounts, which can belong to multiple customers, represented by the following tables:
Account <- Account_Customer -> Customer
The customers, in turn, can own multiple cars:
Customer <- Customer_Car -> Car
In addition, the customers can own many pets:
Customer <- Customer_Pet -> Pet
Now I am trying to come up with the most efficient query to answer the following question:
Get a list of accounts where none of the account owners have a "Cat" and none of the account owners drive a "Dodge".
The script below sets up the tables and some sample data. Please note that in real life, these tables will have 10's of millions of records, so I am trying to come up with the most efficient way to answer this question. So far I was only able to do it by accessing the same tables multiple times.
Setup script:
USE tempdb;
-- Create tables
IF OBJECT_ID('Account') IS NOT NULL DROP TABLE Account;
CREATE TABLE Account (AccountId INT, AccountName VARCHAR(24))
IF OBJECT_ID('Customer') IS NOT NULL DROP TABLE Customer;
CREATE TABLE Customer (CustomerId INT, CustomerName VARCHAR(24))
IF OBJECT_ID('Pet') IS NOT NULL DROP TABLE Pet;
CREATE TABLE Pet (PetId INT, PetName VARCHAR(24))
IF OBJECT_ID('Car') IS NOT NULL DROP TABLE Car;
CREATE TABLE Car (CarId INT, CarName VARCHAR(24))
IF OBJECT_ID('Account_Customer') IS NOT NULL DROP TABLE Account_Customer;
CREATE TABLE Account_Customer (AccountId INT, CustomerId INT)
IF OBJECT_ID('Customer_Pet') IS NOT NULL DROP TABLE Customer_Pet;
CREATE TABLE Customer_Pet (CustomerId INT, PetId INT)
IF OBJECT_ID('Customer_Car') IS NOT NULL DROP TABLE Customer_Car;
CREATE TABLE Customer_Car (CustomerId INT, CarId INT)
-- Populate data
INSERT [dbo].[Account]([AccountId], [AccountName])
VALUES (1, 'Account1'), (2, 'Account2')
INSERT [dbo].[Customer]([CustomerId], [CustomerName])
VALUES (1, 'Customer1'), (2, 'Customer2'), (3, 'Customer3'), (4, 'Customer4')
INSERT [dbo].[Pet]([PetId], [PetName])
VALUES (1, 'Cat1'), (2, 'Cat2'), (3, 'Dog3'), (4, 'Dog4')
INSERT [dbo].[Car]([CarId], [CarName])
VALUES (1, 'Ford1'), (2, 'Ford2'), (3, 'Kia3'), (4, 'Dodge4')
INSERT [dbo].[Account_Customer] ([AccountId], [CustomerId])
VALUES (1,1), (1,2), (2, 2), (2,3), (2,4)
INSERT [dbo].[Customer_Pet] ([CustomerId], [PetId])
VALUES (2,3), (3,1), (3, 2), (4,3), (4,4)
INSERT [dbo].[Customer_Car] ([CustomerId], [CarId])
VALUES (1,2), (2,2), (3,1), (3, 2), (3, 4)
--SELECT * FROM [dbo].[Account] AS [A]
--SELECT * FROM [dbo].[Customer] AS [C]
--SELECT * FROM [dbo].[Pet] AS [P]
--SELECT * FROM [dbo].[Car] AS [C]
--SELECT * FROM [dbo].[Account_Customer] AS [AC]
--SELECT * FROM [dbo].[Customer_Pet] AS [CP]
--SELECT * FROM [dbo].[Customer_Car] AS [CC]
-- Bring all the data together to see what we have (denormalized)
SELECT [A].[AccountId], [A].[AccountName],
[C].[CustomerId], [C].[CustomerName],
[CP].[PetId], [P].[PetName],
[C2].[CarId], [C2].[CarName]
FROM [dbo].[Customer] AS [C]
JOIN [dbo].[Account_Customer] AS [AC] ON [AC].[CustomerId] = [C].[CustomerId]
JOIN [dbo].[Account] AS [A] ON [A].[AccountId] = [AC].[AccountId]
LEFT JOIN [dbo].[Customer_Pet] AS [CP] ON [CP].[CustomerId] = [C].[CustomerId]
LEFT JOIN [dbo].[Pet] AS [P] ON [P].[PetId] = [CP].[PetId]
LEFT JOIN [dbo].[Customer_Car] AS [CC] ON [CC].[CustomerId] = [C].[CustomerId]
LEFT JOIN [dbo].[Car] AS [C2] ON [C2].[CarId] = [CC].[CarId]
ORDER BY [A].[AccountId], [AC].[CustomerId]
And here is the query, which answers my question, but I suspect it's inefficient on a large number of records. Is there a better way?
-- This should only return Account1
SELECT DISTINCT
[A].[AccountId],
[A].[AccountName]
FROM [dbo].[Customer] AS [C]
JOIN [dbo].[Account_Customer] AS [AC] ON [AC].[CustomerId] = [C].[CustomerId]
JOIN [dbo].[Account] AS [A] ON [A].[AccountId] = [AC].[AccountId]
EXCEPT
SELECT -- get Accounts where owner has a "Cat" or drives a "Dodge"
[A].[AccountId],
[A].[AccountName]
FROM [dbo].[Customer] AS [C]
JOIN [dbo].[Account_Customer] AS [AC] ON [AC].[CustomerId] = [C].[CustomerId]
JOIN [dbo].[Account] AS [A] ON [A].[AccountId] = [AC].[AccountId]
WHERE
(
EXISTS (SELECT TOP (1) 1
FROM [dbo].[Customer] AS [C2]
JOIN [dbo].[Customer_Pet] AS [CP2] ON [CP2].[CustomerId] = [C2].[CustomerId]
JOIN [dbo].[Pet] AS [P2] ON [P2].[PetId] = [CP2].[PetId]
WHERE [C2].[CustomerId] = [C].[CustomerId] -- correlation
AND [P2].[PetName] LIKE 'Cat%'
)
OR
EXISTS (SELECT TOP (1) 1
FROM [dbo].[Customer] AS [C2]
JOIN [dbo].[Customer_Car] AS [CP2] ON [CP2].[CustomerId] = [C2].[CustomerId]
JOIN [dbo].[Car] AS [P2] ON [P2].[CarId] = [CP2].[CarId]
WHERE [C2].[CustomerId] = [C].[CustomerId] -- correlation
AND [P2].[CarName] LIKE 'Dodge%'
)
)
Sorry if this is obvious, but please observe that the query below will not work (because it answers slightly different question - return accounts where AT LEAST ONE OWNER does not have a "Cat" and does not drive a "Dodge":
-- Does not work:
SELECT DISTINCT
[A].[AccountId],
[A].[AccountName]
FROM [dbo].[Customer] AS [C]
JOIN [dbo].[Account_Customer] AS [AC] ON [AC].[CustomerId] = [C].[CustomerId]
JOIN [dbo].[Account] AS [A] ON [A].[AccountId] = [AC].[AccountId]
WHERE
(
NOT EXISTS (SELECT TOP (1) 1
FROM [dbo].[Customer] AS [C2]
JOIN [dbo].[Customer_Pet] AS [CP2] ON [CP2].[CustomerId] = [C2].[CustomerId]
JOIN [dbo].[Pet] AS [P2] ON [P2].[PetId] = [CP2].[PetId]
WHERE [C2].[CustomerId] = [C].[CustomerId] -- correlation
AND [P2].[PetName] LIKE 'Cat%'
)
AND
NOT EXISTS (SELECT TOP (1) 1
FROM [dbo].[Customer] AS [C2]
JOIN [dbo].[Customer_Car] AS [CP2] ON [CP2].[CustomerId] = [C2].[CustomerId]
JOIN [dbo].[Car] AS [P2] ON [P2].[CarId] = [CP2].[CarId]
WHERE [C2].[CustomerId] = [C].[CustomerId] -- correlation
AND [P2].[CarName] LIKE 'Dodge%'
)
)
I must say, in a real database I would be very suspicious of all these Many:Many relationships. Can an Account be owned by multiple Customers, each of whom can own multiple Accounts? Equally can a Cat or a Pet have multiple owners?
Be that as it may: you can express your query like this:
You want all Accounts...
for which there do not exist Account_Customers...
Where those Customers are in the set of Customers who own a Cat...
... or a Dodge
SELECT *
FROM Account a
WHERE NOT EXISTS (
SELECT ac.CustomerId
FROM Account_Customer ac
WHERE ac.AccountId = a.AccountId
INTERSECT
(
SELECT cp.CustomerId
FROM Customer_Pet cp
JOIN Pet p ON p.PetId = cp.PetId
WHERE p.PetName LIKE 'Cat%'
UNION ALL
SELECT cc.CustomerId
FROM Customer_Car cc
JOIN Car c ON c.CarId = cc.CarId
WHERE c.CarName LIKE 'Dodge%'
)
)
db<>fiddle
It's too late for a more in-depth answer, so here's a quick and dirty one with a temp table.
Mind you it's not as bad as it looks, many times I've had simple queries on temp tables massively outperform large, interesting (from a mathematic point of view) queries.
Also, a question about performance is never simple to answer. Of special interest is the fact that you mention millions of rows and need for performance while your query uses a like operator on some text column. At least the % is in the end, so it's still SARGable. Will this column have an index? That will probably make a difference.
Here (done blind, hopefully no errors):
create table #forbidden
(
CustomerId int primary key
)
insert #forbidden(CustomerId)
select CustomerId from Customer C
where
exists(select 1 from Customer_Pet CP where CP.CustomerId=C.CustomerId and CP.[PetName] LIKE 'Cat%')
or exists(select 1 from Customer_Car CC where CC.CustomerId=C.CustomerId and CC.[CarName] LIKE 'Dodge%')
select * from Account A
where not exists
(
select 1
from Account_Customer AC
where
AC.CustomerId=A.CustomerId
and AC.CustomerId in (select CustomerId from #forbidden)
)

How is this SQL sub-query correctly vectorising?

Sample data:
CREATE TABLE Departments (
Code INTEGER PRIMARY KEY,
Name varchar(255) NOT NULL ,
Budget decimal NOT NULL
);
CREATE TABLE Employees (
SSN INTEGER PRIMARY KEY,
Name varchar(255) NOT NULL ,
LastName varchar(255) NOT NULL ,
Department INTEGER NOT NULL ,
foreign key (department) references Departments(Code)
)
INSERT INTO Departments(Code,Name,Budget) VALUES(14,'IT',65000);
INSERT INTO Departments(Code,Name,Budget) VALUES(37,'Accounting',15000);
INSERT INTO Departments(Code,Name,Budget) VALUES(59,'Human Resources',240000);
INSERT INTO Departments(Code,Name,Budget) VALUES(77,'Research',55000);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('123234877','Michael','Rogers',14);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('152934485','Anand','Manikutty',14);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('222364883','Carol','Smith',37);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('326587417','Joe','Stevens',37);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('332154719','Mary-Anne','Foster',14);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('332569843','George','O''Donnell',77);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('546523478','John','Doe',59);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('631231482','David','Smith',77);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('654873219','Zacary','Efron',59);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('745685214','Eric','Goldsmith',59);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('845657245','Elizabeth','Doe',14);
INSERT INTO Employees(SSN,Name,LastName,Department) VALUES('845657246','Kumar','Swamy',14);
Problem: "Select the names of departments with more than two employees."
Wikibooks solution:
/*With subquery*/
SELECT D.Name FROM Departments D
WHERE 2 <
(
SELECT COUNT(*)
FROM Employees
WHERE Department = D.Code
);
My question: How does this solution work? That is, how does MSSQL know which values in Departments are to be kept from the sub-query? I can't see any way that the condition WHERE Department = D.Code can return a result that is ordered in a useful way to the outer query. I don't think that this is a fluke, I think that I just don't understand how SQL is vectorised.
This is called a correlated subquery.
That is to say, the inner query is correlated to the outer one by use of an outer reference. In this case, that is D.Code. Therefore the subquery is being calculated for every row of D.
It's not a matter of ordering, in fact this query can return results in any order. But the result from the subquery must be greater than 2 otherwise the WHERE predicate fails.
SELECT D.Name FROM Departments D -- Departments has been aliased as D
WHERE 2 <
(
SELECT COUNT(*)
FROM Employees
WHERE Department = D.Code -- Here the inner query is being limited by
-- the reference to the outer D table
);
I would probably use ... > 2 rather than 2 < ...
Side point: It's better to always use an explicit table reference in subqueries, eg e.Department = D.Code, because otherwise you could misspell a column and end up referring to an outer column instead of an inner column, and the correlation wouldn't work properly

How to count missed Parts from Table Trade and that Exist On Table Parts?

I work on sql server 2012 I face issue I can't get missed parts from trade code table and exist on table parts .
first I upload data of plid and codetypeid on table search data .
second I get related parts from table parts based on plid .
third i get missed parts from table trade code .
meaning I need to get parts exist on table parts and related to table search data and not exist on table trade code
Data Sample :
create table #searchdata
(
plid int,
codetypeid int
)
insert into #searchdata
(plid,codetypeid)
values
(84459,877490)
create table #parts
(
partid int,
plid int
)
insert into #parts(partid,plid)
values
(758901,84459),
(808091,84459),
(509030,84459),
(7090321,84459),
(32453,84459),
(45563,84459)
create table #tradecode
(
partid int,
codetypeid int
)
insert into #tradecode(partid,codetypeid)
values
(758901,877490),
(808091,877490)
select p.plid,s.codetypeid,count(p.partid) as countmissingParts
from #parts p
inner join #searchdata s on s.plid=p.plid
left join #tradecode t on t.codetypeid=s.codetypeid
where t.partid is null
group by p.plid,s.codetypeid
drop table #searchdata
drop table #parts
drop table #tradecode
what I try :
select p.plid,s.codetypeid,count(p.partid) as countmissingParts
from #parts p
inner join #searchdata s on s.plid=p.plid
left join #tradecode t on t.partid=s.plid
where t.partid is null
group by p.plid,s.codetypeid
Expected Result
plid codetypeid countmissingParts
84459 877490 4
You were very close on your what you tried query... Give this a try. Your LEFT JOIN, you joined the partid to the plid... Should have joined p.partid=t.partid
select p.plid,s.codetypeid,count(p.partid) as countmissingParts
from #parts p
inner join #searchdata s on s.plid=p.plid
left join #tradecode t on p.partid=t.partid
where t.partid is null
group by p.plid,s.codetypeid

Show all and only rows in table 1 not in table 2 (using multiple columns)

I have one table (Table1) that has several columns used in combination: Name, TestName, DevName, Dept. When each of these 4 columns have values, the record is inserted into Table2. I need to confirm that all of the records with existing values in each of these fields within Table1 were correctly copied into Table 2.
I have created a query for it:
SELECT DISTINCT wr.Name,wr.TestName, wr.DEVName ,wr.Dept
FROM table2 wr
where NOT EXISTS (
SELECT NULL
FROM TABLE1 ym
WHERE ym.Name = wr.Name
AND ym.TestName = wr. TestName
AND ym.DEVName = wr.DEVName
AND ym. Dept = wr. Dept
)
My counts are not adding up, so I believe that this is incorrect. Can you advise me on the best way to write this query for my needs?
You can use the EXCEPT set operator for this one if the table definitions are identical.
SELECT DISTINCT ym.Name, ym.TestName, ym.DEVName, ym.Dept
FROM table1 ym
EXCEPT
SELECT DISTINCT wr.Name, wr.TestName, wr.DEVName, wr.Dept
FROM table2 wr
This returns distinct rows from the first table where there is not a match in the second table. Read more about EXCEPT and INTERSECT here: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/set-operators-except-and-intersect-transact-sql?view=sql-server-2017
Your query should do the job. It checks anything that are in Table1, but not Table2
SELECT ym.Name, ym.TestName, ym.DEVName, ym.Dept
FROM Table1 ym
WHERE NOT EXISTS (
SELECT 1
FROM table2
WHERE ym.Name = Name AND ym.TestName = TestName AND ym.DEVName = DEVName AND ym. Dept = Dept
)
If the structure of both tables are the same, EXCEPT is probably simpler.
IF OBJECT_ID(N'tempdb..#table1') IS NOT NULL drop table #table1
IF OBJECT_ID(N'tempdb..#table2') IS NOT NULL drop table #table2
create table #table1 (id int, value varchar(10))
create table #table2 (id int)
insert into #table1(id, value) VALUES (1,'value1'), (2,'value2'), (3,'value3')
--test here. Comment next line
insert into #table2(id) VALUES (1) --Comment/Uncomment
select * from #table1
select * from #table2
select #table1.*
from #table1
left JOIN #table2 on
#table1.id = #table2.id
where (#table2.id is not null or not exists (select * from #table2))

insert results of join in SQL server 2005

I have two tables with more than 800 rows in each.Table names are 'education' and 'sanitation'.The column name 'ID' is common in both the tables.Now i want to join these both tables as full outer join and i want to save the results of this table as a new table.I can join it very easily,But how to save those data's as a new table.Please help Me.
select * into bc from education e join sanitation s on e.id=s.id
I have around 30 columns in each table.So i can not explicitly create table schema for a new table.
I want all the columns from both tables.I have 20 tables with each 800 rows.From this 20 tables i want to make one master table taking 'ID' as primary key in all.
Sample Code:
Table one:
create table dummy1(
id int , fname varchar(50)
)
insert into dummy1 (id,fname) values (1,'aaa')
insert into dummy1 (id,fname) values (2,'bbb')
insert into dummy1 (id,fname) values (3,'ccc')
insert into dummy1 (id,fname) values (3,'ccc')
Table Two
create table dummy2(
id int , lname varchar(50)
)
insert into dummy2 (id,lname) values (1,'abc')
insert into dummy2 (id,lname) values (2,'pqr')
insert into dummy2 (id,lname) values (3,'mno')
Now Create new table 3
create table dummy3(
id int , fname varchar(50),lname varchar(50)
)
Insert Query for table 3 look like
insert into dummy3 (id,fname,lname)
(select a.id,a.fname,b.lname from dummy1 a inner join dummy2 b on a.id=b.id)
Table 3 will contain table1, table2 data
Follow below:
SELECT t1.Column1, t2.Columnx
INTO DestinationTable
FROm education t1
INNER JOIN sanitation t2 ON t1.Id = t2.Id
EDIT:
SELECT * will not work for you because you have a column ID which exists in both the tables. So the above solution will work you.
EDIT:
1- You can temporarily rename the Id column in one table, then try
2- SELECT *
INTO DestinationTable
FROm education t1
INNER JOIN sanitation t2 ON t1.Id = t2.Id
3- Revert the column name back to Id.

Resources