Sql server subquery to be rewritten using Joins - sql-server

I have this pasted subquery and i need to rewrite it using only joins ( no subquery).
Tried multiple times for close to a month but in vain.
Request you to help me out.
SELECT * FROM wp_user WHERE userId NOT IN
(SELECT u.userId FROM wp_user as u, wp_luncher as i, wp_subscription as s
WHERE u.userId = i.luncherId
and i.luncherId = s.luncherid)
and CreationDate between '20181001' and '20181015';
Tables involved :-
CREATE TABLE wp_user (
userId int identity(1,1) PRIMARY KEY NOT NULL,
userName varchar(20) NOT NULL,
CreationDate date NOT NULL
);
CREATE TABLE wp_luncher (
luncherId int PRIMARY KEY NOT NULL,
parentId int FOREIGN KEY REFERENCES wp_user(userId)
);
CREATE TABLE wp_subscription (
SubId int PRIMARY KEY NOT NULL,
luncherId int FOREIGN KEY REFERENCES wp_luncher(luncherId)
);

Try this query.
You can use OUTER APPLY.
SELECT *
FROM wp_user wp
OUTER APPLY
(
SELECT i.luncherId
FROM wp_luncher as i, wp_subscription as s
WHERE i.luncherId = wp.userId
and i.luncherId = s.luncherid
) I
WHERE I.luncherId IS NOT NULL
and CreationDate between '20181001' and '20181015';
Another option inserts the subquery's data into a table variable.
DECLARE #TempTable AS TABLE (UserId INT)
INSERT INTO #TempTable
SELECT i.luncherId
FROM wp_luncher as i, wp_subscription as s
WHERE i.luncherId = s.luncherid
SELECT *
FROM wp_user wp
LEFT JOIN #TempTable I ON I.UserId = wp.userId
WHERE I.UserId IS NOT NULL
and CreationDate between '20181001' and '20181015';

Related

Why does a join on ROW_NUMBER() between common table expressions result in a Cartesian product?

I am joining two common table expressions (cte) based on an id created within each cte using ROW_NUMBER(). The resulting execution plan includes a Merge Join where the Estimated Rows are 13,530,000,000 in my case; and the actual number of rows ends up being about 13,000.
Below is an example of what I am trying to do, and this results in a similar execution plan with 1,200,000,000 estimated rows in the Merge Join, and 12,000 actual rows.
I am stuck understanding why what seems like a simple join is creating a Cartesian product.
IF OBJECT_ID('tempdb..#Product') IS NOT NULL DROP TABLE #Product;
IF OBJECT_ID('tempdb..#Product_Id') IS NOT NULL DROP TABLE #Product_Id;
CREATE TABLE #Product (
[rowId] int identity primary key,
[id] int
)
CREATE TABLE #Product_Id (
[id] int primary key
)
DECLARE #Id int
SET #Id = 500000
WHILE #Id <= 600000
BEGIN
INSERT INTO #Product_Id
VALUES (#Id)
SET #Id += 1
END
SET #Id = 1
WHILE #Id <= 12000
BEGIN
INSERT INTO #Product
VALUES (NULL)
SET #Id += 1
END
WITH
PRODUCT_ID_CTE AS (
SELECT [id],
ROW_NUMBER() OVER(ORDER BY [id]) AS [rn]
FROM #Product_Id
),
PRODUCT_CTE AS (
SELECT [id],
ROW_NUMBER() OVER(ORDER BY [rowId]) AS [rn]
FROM #Product
)
SELECT ISNULL(a.[id], b.[id]) AS [id]
FROM PRODUCT_CTE a
JOIN PRODUCT_ID_CTE b
ON a.[rn] = b.[rn]
Execution plan with CTE
Execution plan with sub queries

How to find missing pairs in many-to-many table?

Given: Users and Groups, as well as many-to-many GroupUsers table.
It is necessary that each pair of users has its own paired group, provided that there can be groups with more or less users.
How to check and create the missing paired groups?
Link to SQL Fiddle
create table dbo.Users (
Id int not null,
Name nvarchar(50) not null,
constraint PK_Users primary key clustered (Id)
);
create table dbo.Groups (
Id int not null,
Name nvarchar(50) not null,
constraint PK_Groups primary key clustered (Id)
);
create table dbo.GroupUsers (
GroupId int not null ,
UserId int not null ,
constraint PK_GroupUsers primary key clustered (GroupId, UserId),
constraint FK_GroupUsers_GroupId foreign key (GroupId) references dbo.Groups (Id),
constraint FK_GroupUsers_UserId foreign key (UserId) references dbo.Users (Id)
);
insert into dbo.Users values (1, 'Anna'), (2, 'Berta'), (3, 'Carlie'), (4, 'Dana'), (5, 'Emil');
insert into dbo.Groups values (1, 'Anna-Berta'), (2, 'Anna-Carlie'), (3, 'Anna-Berta-Carlie');
insert into dbo.GroupUsers values
(1,1), (1, 2), -- 'Anna-Berta' group
(2,1), (2, 3), -- 'Anna-Carlie' group
(3,1), (3, 2), (3, 3); -- 'Anna-Berta-Carlie' group
How to find and create missing "Pair Group" for user Anna?
So Anna has a Pair Group with all other users
and all the Anna's Pair Groups must have exactly two users,
although any user including Anna may have a group which includes more or less than two users.
UPDATE 2019-12-15:
So here (Link to SQL Fiddle) is my favorite solution (so far) of how to find missing paired groups and their users (Thanks to answer from #Kari F.)
declare #UserId int = 1;
with cte as
(
select
VirtualGroupId = row_number() over(order by p.Id desc) * -1,
GroupName = concat( u.Name, '_', p.Name),
CurrentUserId = u.Id,
OtherUserId = p.Id
from
dbo.Users u inner join dbo.Users p on u.Id = #UserId and p.Id <> #UserId
where
concat('_', u.Id, '_', p.Id) not in
(
select
(
select concat('_', gu.UserId)
from dbo.GroupUsers gu
where gu.GroupId = g.Id
order by case when gu.UserId = #UserId then 0 else 1 end
for xml path ('')
)
from
dbo.Groups g
)
)
select VirtualGroupId, GroupName, UserId = CurrentUserId from cte
union all
select VirtualGroupId, GroupName, UserId = OtherUserId from cte
order by VirtualGroupId, UserId
What about this simple solution:
select u.Id, p.Id
from dbo.Users u
cross join dbo.Users p
where u.Id = 1 and p.Id <> 1
and u.Name + '-' + p.Name not in (select g.Name from dbo.Groups g);
http://sqlfiddle.com/#!18/3c30f/16
Second solution taking into account you comment
with required_groups as (
select u.Id as userId, p.Id pairId
from dbo.Users u
cross join dbo.Users p
where u.Id = 1 and p.Id <> 1
),
existing_groups as (
select annas_groups.UserId as userId, pairs_groups.UserId as pairId
from (
select *
from GroupUsers gu
where gu.UserId = 1
and gu.GroupId in (
select x.GroupId from GroupUsers x
group by x.GroupId
having count(*) = 2
)
) annas_groups
inner join (
select *
from GroupUsers gu
where
gu.UserId <> 1
and gu.GroupId in (
select x.GroupId from GroupUsers x
group by x.GroupId
having count(*) = 2
)
) pairs_groups on annas_groups.GroupId = pairs_groups.GroupId
)
select *
from required_groups rg
where not exists (
select 1
from existing_groups eg
where eg.UserId = rg.UserId
and eg.PairId = rg.PairId
)
http://sqlfiddle.com/#!18/3c30f/34
A bit more complex but still readable, I think.
I have tried to solve the problem using a loop approach, this is because the id from the groups table must be supplied and is not an identity column otherwise the implementation becomes simpler.
I've also used a keyuser variable to handle the fact that it is "Anna" who must be paired and not some other user. This avoids simply hard coding the userid value.
create table #Users (
Id int not null,
Name nvarchar(50) not null,
constraint PK_Users primary key clustered (Id)
);
create table #Groups (
Id int not null,
Name nvarchar(50) not null,
constraint PK_Groups primary key clustered (Id)
);
create table #GroupUsers (
GroupId int not null ,
UserId int not null ,
constraint PK_GroupUsers primary key clustered (GroupId, UserId),
constraint FK_GroupUsers_GroupId foreign key (GroupId) references #Groups (Id),
constraint FK_GroupUsers_UserId foreign key (UserId) references #Users (Id)
);
insert into #Users values (1, 'Anna'), (2, 'Berta'), (3, 'Carlie'), (4, 'Dana'), (5, 'Emil');
insert into #Groups values (1, 'Anna-Berta'), (2, 'Anna-Carlie'), (3, 'Anna-Berta-Carlie');
insert into #GroupUsers values
(1,1), (1, 2), -- 'Anna-Berta' group
(2,1), (2, 3), -- 'Anna-Carlie' group
(3,1), (3, 2), (3, 3); -- 'Anna-Berta-Carlie' group
declare #sql nvarchar(max) = '';
declare #nextgroupid int = 0;
declare #keyuser int = 0;
select #keyuser = ID from #Users where [Name]='Anna';
while exists (select 1 from #Users u inner join #Users u2 on u.Id=1 where u2.Id not in (select userid from #GroupUsers))
begin
select #nextgroupid = MAX(id)+1 from #Groups;
set #sql = 'insert #groups (id, [name]) select top 1 ' + CAST(#nextgroupid as nvarchar(3)) + ', u.[name] + ''-'' + u2.[name] as missingusergroup from #Users u inner join #Users u2 on u.Id= ' + CAST(#keyuser as nvarchar(3)) + ' where u2.Id not in (select userid from #GroupUsers) order by u2.id;';
exec(#sql);
set #sql = 'insert #groupusers (groupid, userid) select top 1 ' + CAST(#nextgroupid as nvarchar(3)) + ', u2.id from #Users u inner join #Users u2 on u.Id= ' + CAST(#keyuser as nvarchar(3)) + ' where u2.Id not in (select userid from #GroupUsers) union select top 1 '+ CAST(#nextgroupid as nvarchar(3)) +', u.id from #Users u where u.id= ' + CAST(#keyuser as nvarchar(3)) + ' order by u2.id;';
exec(#sql);
end
select * from #GroupUsers;
select * from #Groups;
drop table #Groups, #GroupUsers, #Users;

How to delete and insert records using OUTPUT in T-SQL?

I am trying to delete some records and inserting into other table at the same time. While deleting it's a self join on that table which checks some conditions. I want to do both delete and inset operations using OUTPUT clause.
Code:
DELETE dbo.Test
OUTPUT DELETED.Recipient_Key,
DELETED.Home_Dt,
DELETED.Batch_No,
DELETED.Brand_Cd,
DELETED.Campaign_Cd,
DELETED.Campaign_Nm,
DELETED.CampaignType_Cd
INTO dbo.Error
FROM dbo.Test AS PR1
INNER JOIN Staging.dbo.Test AS PR2
ON PR2.Recipient_Key = PR1.Recipient_Key
AND PR2.Batch_No = PR1.Batch_No
AND PR2.Home_Dt <> PR1.Home_Dt;
With the self join you need to specify an alias.
drop table if exists #test;
create table #test (
Id int not null primary key clustered identity(1, 1)
, SomeColumn varchar(255) not null
);
drop table if exists #error;
create table #error (
Id int not null primary key clustered
, SomeColumn varchar(255) not null
);
insert into
#test (SomeColumn)
values
('A'), ('B'), ('C');
select * from #test;
select * from #error;
delete a
output
Deleted.Id, Deleted.SomeColumn
into
#error (Id, SomeColumn)
from
#test as a
inner join
#test as b
on
a.Id = b.Id
and a.Id % 2 = 1;
select * from #test;
select * from #error;

Can't put CTE result to temporary table

I got a SP where I need to put CTE result to a temporary table so I can use the temporary table later on in SP. Since I am new so I am experiencing difficulty. Please help me out.
CREATE TABLE #TempTable1 (
tmp_id INT NULL,
tmp_parent_id INT NULL,
temp_level VARCHAR(50) NULL,
temp_order VARCHAR(50) NULL,
temp_promoter_ID INT NULL
);
DECLARE #promoterid INT = (
SELECT
p.promoterid
FROM dbo.Promoters p
INNER JOIN dbo.UserProfile u
ON u.UserId = p.UserProfileId
WHERE u.UserName = #Username
);
;WITH Empl_Tab (Id, ParentId, LEVEL, [Order], promoterid) AS (
SELECT
promoters.UserProfileId AS ID,
promoters.Level1 AS ParentID,
0 AS LEVEL,
CONVERT([VARCHAR](MAX), promoters.PromoterId) AS [Order],
promoters.PromoterId
FROM promoters
WHERE Promoters.PromoterId = #promoterid
UNION ALL
SELECT
p.UserProfileId AS ID,
p.Level1 AS ParentID,
Empl_Tab.LEVEL + 1 AS LEVEL,
Empl_Tab.[Order] + CONVERT([VARCHAR](30), p.PromoterId) AS [Order],
p.PromoterId
FROM Promoters p
INNER JOIN Empl_Tab
--inner join dbo.UserProfile u on u.UserId= Promoters.UserProfileId
ON Empl_Tab.promoterid = p.Level1
--where p.Active!=2
)
--select Id, ParentId, LEVEL,[Order],promoterid from Empl_Tab
INSERT INTO #TempTable1 --(tmp_id, tmp_parent_id, temp_level, temp_order, temp_promoter_ID )
SELECT *
FROM Empl_Tab;
Now I like to put the Emp1_Tab result to the temporary table and like to use the temporary table data later on in this same SP.
Procedure TeamCustomersListNew, Line 42 String or binary data would be truncated.
The above error message states that one of the values you're inserting exceeds the max length. The temp_order column of the temp table only allows for 50 characters. You may want to increase it or use VARCHAR(MAX) instead:
CREATE TABLE #TempTable1 (
tmp_id INT NULL,
tmp_parent_id INT NULL,
temp_level INT NULL,
temp_order VARCHAR(MAX) NULL,
temp_promoter_ID INT NULL
);
Additionally, temp_level should be INT.

Removing duplicate rows while also updating relations

My data is set up as follows:
CREATE TABLE TableA
(
id int IDENTITY,
name varchar(256),
description varchar(256)
)
CREATE TABLE TableB
(
id int IDENTITY,
name varchar(256),
description varchar(256)
) --unique constraint on name, description
CREATE TABLE TableA_TableB
(
idA int,
idB int
) --composite key referencing TableA and TableB
The situation is that I have many duplicate records in TableB that violate the unique constraint, and those duplicate records are referenced in TableA_TableB. So I'm trying to remove those records, which is simple enough (using the following CTE), but what would be the best way to update the records in TableA_TableB to reflect this change, i.e, have the TableA_TableB records reference the same ID in TableB as opposed to different IDs for each of the duplicates?
;WITH cte
AS (SELECT ROW_NUMBER() OVER (PARTITION BY [Name], [Description]
ORDER BY ( SELECT 0)) RN
FROM TableB)
DELETE FROM cte
WHERE RN = 1
Note: changed b.RowNum=1 to b.RowNum>1
First, you should try with ROLLBACK and then, if it's OK, uncomment COMMIT (this script wasn't tested):
DECLARE #UpdatedRows TABLE(ID INT PRIMARY KEY);
BEGIN TRANSACTION;
;WITH Base
AS(
SELECT ROW_NUMBER() OVER (PARTITION BY [Name], [Description] ORDER BY ( SELECT 0)) RowNum,
MIN(id) OVER(PARTITION BY [Name], [Description]) AS NewID,
ID -- Old ID
FROM TableB
),TableB_RowsForUpdate
AS(
SELECT *
FROM Base b
WHERE b.RowNum>1
)
UPDATE target
SET IDB=b.NewID
OUTPUT deleted.IDB INTO #UpdatedRows
FROM TableA_TableB target
INNER JOIN TableB_RowsForUpdate b ON target.IDB=b.ID;
DELETE b
FROM TableB b INNER JOIN #UpdatedRows upd ON b.ID=upd.ID;
ROLLBACK;
-- COMMIT;

Resources