query issue with a LEFT JOIN - sql

Requirement : Get the All users in the Login Table + and their LATEST Login Record from UserLogin Table for provided tenant
Current Query :
SELECT [USER].UserName , UserLogin.AttemptDate , UserLogin.LogoutDate
FROM [User] LEFT JOIN UserLogin
ON [User].UserId = UserLogin.UserId
WHERE [User].TenantId=3
ORDER BY UserLogin.LogoutDate desc
Issue : Repeats the User Name not Distinct

SELECT
a.UserName,
c.AttemptDate,
c.LogoutDate
FROM
[User] a
LEFT JOIN
(
SELECT UserId, MAX(LogoutDate) AS maxdate
FROM UserLogin
GROUP BY UserId
) b ON a.UserId = b.UserId
LEFT JOIN
UserLogin c ON b.UserId = c.UserId AND b.maxdate = c.LogoutDate
WHERE
a.TenantId = 3
ORDER BY
c.LogoutDate DESC
This query has the additional advantage of being DBMS-agnostic (except for the brackets around User), and does not rely on window-functions or external variables.

It seems that each UserId has more than one match in UserLogin, you have to prioritize records for a given UserId from the UserLogin and choose the one that you want in the result set:
SELECT [USER].UserName , x.AttemptDate , .LogoutDate
FROM [User]
LEFT JOIN
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY <PriorityCondition>) Priority
FROM UserLogin
) x
ON [User].UserId = x.UserId AND x.Priority = 1
WHERE [User].TenantId=3
ORDER BY x.LogoutDate desc
Like in this example:
DECLARE #User TABLE (UserId INT, UserName VARCHAR(100), TenantId INT)
INSERT #User VALUES (1, 'a', 3), (2, 'b', 3)
DECLARE #UserLogin TABLE (UserId INT, UserName VARCHAR(100), AttemptDate DATETIME, LogoutDate DATETIME)
INSERT #UserLogin VALUES (1, 'a', GETDATE(), GETDATE()),
(2, 'b', GETDATE(), GETDATE()),
(2, 'b', GETDATE(), DATEADD(DAY, -1, GETDATE()))
SELECT y.UserName , x.AttemptDate , x.LogoutDate
FROM #User y
LEFT JOIN
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY LogoutDate DESC) Priority
FROM #UserLogin
) x
ON y.UserId = x.UserId AND x.Priority = 1
WHERE y.TenantId=3
ORDER BY x.LogoutDate DESC

with cte as(
SELECT [USER].UserId , max(UserLogin.LogoutDate ) [LogoutDate]
FROM [User] JOIN UserLogin
ON [User].UserId = UserLogin.UserId
WHERE [User].TenantId=3
group by [USER].UserId
)
select U.UserName,L.AttemptDate , L.LogoutDate from cte C , UserLogin L,
[User] U
where C.LogoutDate=L.LogoutDate
and C.UserId=L.UserId
and U.UserId=L.UserId

If I understood correctly
SELECT [USER].UserName , UserLogin.AttemptDate , MAX(UserLogin.LogoutDate) FROM [User] LEFT JOIN UserLogin ON [User].UserId = UserLogin.UserId WHERE [User].TenantId=3 ORDER BY UserLogin.LogoutDate desc

Related

Selecting multiple fields from row with max value of column, per group

I'm quite certain I've painted myself into a corner and I can't figure my way out.
The Users table and OrderHistories tables both have 1+ million records:
SELECT
u.Id ,
u.Email AS EmailAddress ,
c.Address_Address1 AS "Address 1" ,
(
SELECT
COUNT(*)
FROM
dbo.OrderHistories oh
WHERE
oh.UserId = u.UserName
) AS NumberOfOrders ,
Carts.SubtotalAmount AS CartTotal ,
(
SELECT
MAX(oh.CreateDate)
FROM
dbo.OrderHistories AS oh
WHERE
oh.UserId = u.Id
) AS LastOrderDate ,
(
SELECT
LastOrders.SubtotalAmount AS LastOrderSubtotal
FROM
(
SELECT
UserId ,
CreateDate ,
SubtotalAmount ,
MAX(CreateDate) OVER ( PARTITION BY UserId ) MyLastOrderDate
FROM
Users u
INNER JOIN dbo.OrderHistories oh
ON u.Id = oh.UserId
) AS LastOrders
WHERE
LastOrders.MyLastOrderDate = LastOrders.CreateDate
AND LastOrders.UserId = u.Id
) AS LastOrderSubtotal
FROM
Users u
INNER JOIN Customers AS c
ON u.Id = c.Id
LEFT JOIN dbo.Carts
ON c.Id = Carts.CustomerId
This particular subquery is my current problem (EXTREMELY inefficient), but I'm not experienced enough to understand exactly why, or how I should be doing it instead (I can't get there from here!):
(
SELECT
LastOrders.SubtotalAmount AS LastOrderSubtotal
FROM
(
SELECT
UserId ,
CreateDate ,
SubtotalAmount ,
MAX(CreateDate) OVER ( PARTITION BY UserId ) MyLastOrderDate
FROM
Users u
INNER JOIN dbo.OrderHistories oh
ON u.Id = oh.UserId
) AS LastOrders
WHERE
LastOrders.MyLastOrderDate = LastOrders.CreateDate
AND LastOrders.UserId = u.Id
) AS LastOrderSubtotal
Anyone mind telling me how terrible I am and then segue right into a suggested improvement?
Just from looking at your query, you may be able to simplify it using cross apply() like so:
select
u.Id
, EmailAddress = u.Email
, [Address 1] = c.Address_Address1
, CartTotal = Carts.SubtotalAmount
, NumberOfOrders = oh.NumberOfOrders
, LastOrderDate = oh.CreateDate
, LastOrderSubtotal = oh.SubtotalAmount
from Users u
inner join Customers AS c
on u.Id = c.Id
left join dbo.Carts
on c.Id = Carts.CustomerId
cross apply (
select top 1
i.CreateDate
, i.SubtotalAmount
, NumberOfOrders = count(*) over (partition by i.UserId)
from dbo.OrderHistories i
where i.UserId = u.Id
order by i.CreateDate desc
) as oh
If you want rows that may not have an OrderHistory, switch to outer apply().
Reference:
apply() - msdn
The power of T-SQL's APPLY operator - Rob Farley
APPLY: It Slices! It Dices! It Does It All! - Brad Shulz

How to set an if on a select for returning select in sql?

I have two tables named User and ParentUser that there is an one to many relation between them and the many side is the ParentUser table.
I have written this select on User table:
select u.* from [User] u inner join ParentUser p on u.UserId=p.UserId where p.ParentId=2440
Now I wanna add another column to u.* containing 0 or 1.0 is for users who have some children in ParentUser and 1 is for those whom dont have any.
How to handle this?
Update
You need LEFT JOIN
SELECT u.*,
CASE
WHEN p.ParentId IS NULL THEN 1
ELSE 0
END AS HasChildren
FROM [User] u
LEFT JOIN (SELECT DISTINCT ParentId
FROM ParentUser
WHERE ParentId IN (SELECT UserId
FROM ParentUser
WHERE parentId = 2440)) p
ON u.UserId = p.ParentId
SELECT DISTINCT
[UserId] = [u].[userid]
,[HasChildren] = CAST(ISNULL([pu].[userid], 0) AS BIT)
FROM
[User] AS [u]
OUTER APPLY
(
SELECT [userid] FROM [ParentUser] WHERE [parentid] = [u].[userid]
) AS [pu];
And, if you want to filter by parent id:
DECLARE #ParentId INT = 2;
SELECT DISTINCT
[UserId] = [u].[userid]
,[HasChildren] = CAST(ISNULL([pu].[userid], 0) AS BIT)
,[ChildrenId] = [pu].[userid]
FROM
[user] AS [u]
OUTER APPLY
(
SELECT [userid], [parentid] FROM [ParentUser] WHERE [parentid] = [u].[userid]
) AS [pu]
WHERE
[pu].[parentid] = #ParentId;
It's fairly easy to determine each user that has children. The following starts out by determining the number of children, but in final output (with other columns from user) simply reduces this to: "Are there any children?"
/************************* Set up sample data *************************/
declare #User table (
UserId int,
UserName varchar(10)
)
insert #User
select 1, 'A' union all
select 2, 'B' union all
select 3, 'C'
declare #ParentUser table (
UserId int,
ParentId int
)
insert #ParentUser
select 3, 1 union all
select 3, 2 union all
select 2, 1
/****************************** Solution ******************************/
;with ChildCounts as (
select p.ParentId, COUNT(*) as ChildCount
from #ParentUser p
group by p.ParentId
), UsersWithHasChildren as (
select u.*,
case
when p.ChildCount > 0 then 1
else 0
end as HasChildren
from #User u
left join ChildCounts p on
p.ParentId = u.UserId
)
select *
from UsersWithHasChildren u
From your duplicate question, the special case of determining which children of a particular parent also have children is a trivial WHERE filter as below.
where u.UserId in (
select p.UserId
from #ParentUser p
where p.ParentId = 1 /*Parametrise this value*/
)

Duplicate value in XMLAGG and RTRIM SQL Query

The Below query brings in duplicate values
SELECT maxuser.userid,
person.displayname,
maxuser.status,
sod_Report.severity,
sod_Report.scenario,
RTRIM(XMLAGG(Xmlelement(E,groupuser.groupname,',').EXTRACT('//text()') ORDER BY groupuser.groupname).GetClobVal(),',') groupname
FROM maxuser
INNER JOIN person
ON maxuser.userid = person.personid
LEFT OUTER JOIN maximo.groupuser
ON maxuser.userid = groupuser.userid
AND maxuser.userid = person.personid
LEFT OUTER JOIN sod_report
ON sod_report.userid = maxuser.userid
AND sod_report.userid = person.personid
WHERE 1 = 1
AND (( maxuser.userid LIKE '%XX%' ))
GROUP BY maxuser.userid,
person.displayname,
maxuser.status,
sod_Report.severity,
sod_Report.scenario
The output is coming as below
USERID DISPLAYNAME GROUPNAME
XXXX Test User A1,A1,B1,B1,C1,C1
Expected output
USERID DISPLAYNAME GROUPNAME
XXXX Test User A1,B1,C1
I have to use RTRIM and XMLAGG since the output of GROUPNAME should be displayed in single line separated by commas. This is for Oracle database , Please advise.
Demo
Add an additional step before the aggregation, that uses ROW_NUMBER.
create table groupuser (groupname varchar2(100));
insert into groupuser (groupname) values ('A1');
insert into groupuser (groupname) values ('A1');
insert into groupuser (groupname) values ('B1');
insert into groupuser (groupname) values ('B1');
insert into groupuser (groupname) values ('C1');
insert into groupuser (groupname) values ('C1');
select listagg (case when rn=1 then groupname end,',') within group (order by groupname)
from (select groupname
,row_number () over (partition by groupname order by null) as rn
from groupuser
)
;
select rtrim(xmlagg(case when rn=1 then xmlelement(e,groupname,',').extract('//text()') end order by groupname).getclobval(),',')
from (select groupname
,row_number () over (partition by groupname order by null) as rn
from groupuser
)
;
SELECT userid,
displayname,
status,
severity,
scenario,
RTRIM(XMLAGG(Xmlelement(E,groupname,',').EXTRACT('//text()') ORDER BY groupname).GetClobVal(),',') groupname
FROM (
SELECT maxuser.userid,
person.displayname,
maxuser.status,
sod_Report.severity,
sod_Report.scenario,
groupuser.groupname,
row_number () over (partition by groupuser.groupname order by null) as rn
FROM maxuser
INNER JOIN person
ON maxuser.userid = person.personid
LEFT OUTER JOIN maximo.groupuser
ON maxuser.userid = groupuser.userid
AND maxuser.userid = person.personid
LEFT OUTER JOIN sod_report
ON sod_report.userid = maxuser.userid
AND sod_report.userid = person.personid
WHERE 1 = 1
AND (( maxuser.userid LIKE '%XX%' ))
) t
GROUP BY userid,
displayname,
status,
severity,
scenario
;
You can use regular expression
select rtrim(regexp_replace('A1,A1,B1,B1,C1,C1','(\w+)(,(\1))+,?','\1,'),',') from dual

Increment id value in SQL Server

In my SQL I have this query and I want to increase id with this insert that have
I don't want to use identity(1,1)
INSERT INTO dbo.tbl_waredetails
(wd_id, wd_mt_code, wd_wa_Id, wd_supply, wd_description, wd_status)
SELECT
(SELECT ISNULL(MAX(wd.wd_id), 0) + 1
FROM dbo.tbl_waredetails AS wd),
dbo.tbl_material.mat_code, #id,
dbo.fun_CompRem(mat_code, -1, #user_id) AS supply,
NULL, 0
FROM
tbl_material
INNER JOIN
dbo.tbl_MatUnit ON dbo.tbl_material.mat_MatUnt_Code = dbo.tbl_MatUnit.Matunt_code
INNER JOIN
dbo.tbl_MatGroup ON dbo.tbl_material.mat_MatGrp_Code = dbo.tbl_MatGroup.MatGrp_Code
but it send always 2 as id
Try below query
INSERT INTO dbo.tbl_waredetails
(wd_id, wd_mt_code, wd_wa_Id, wd_supply, wd_description, wd_status)
SELECT
(SELECT ISNULL(MAX(wd.wd_id), 0)
FROM dbo.tbl_waredetails AS wd)+ (row_number() over (order by wd.wd_id)),
dbo.tbl_material.mat_code, #id,
dbo.fun_CompRem(mat_code, -1, #user_id) AS supply,
NULL, 0
FROM
tbl_material
INNER JOIN
dbo.tbl_MatUnit ON dbo.tbl_material.mat_MatUnt_Code = dbo.tbl_MatUnit.Matunt_code
INNER JOIN
dbo.tbl_MatGroup ON dbo.tbl_material.mat_MatGrp_Code = dbo.tbl_MatGroup.MatGrp_Code
with cte as
(select (SELECT isnull(MAX(wd.wd_id),0) FROM dbo.tbl_waredetails ) as iden,dbo.tbl_material.mat_code,#id,
dbo.fun_CompRem(mat_code,-1,#user_id
)as supply,NULL,0
FROM tbl_material INNER JOIN
dbo.tbl_MatUnit ON dbo.tbl_material.mat_MatUnt_Code = dbo.tbl_MatUnit.Matunt_code INNER JOIN
dbo.tbl_MatGroup ON dbo.tbl_material.mat_MatGrp_Code = dbo.tbl_MatGroup.MatGrp_Code
where ROW_NUMBER ( )
OVER ( order by 1 )=1
union all
select c.iden+1,dbo.tbl_material.mat_code,#id,
dbo.fun_CompRem(mat_code,-1,#user_id
)as supply,NULL,0
FROM tbl_material INNER JOIN
dbo.tbl_MatUnit ON dbo.tbl_material.mat_MatUnt_Code = dbo.tbl_MatUnit.Matunt_code INNER JOIN
dbo.tbl_MatGroup ON dbo.tbl_material.mat_MatGrp_Code = dbo.tbl_MatGroup.MatGrp_Code
join cte as d on 1=1
where ROW_NUMBER ( )
OVER ( order by 1 )!=1
)
INSERT INTO dbo.tbl_waredetails
(
wd_id,
wd_mt_code ,
wd_wa_Id ,
wd_supply ,
wd_description ,
wd_status
)
SELECT iden, dbo.tbl_material.mat_code,#id,supply,NULL,0
FROM CTE

Correlated query: select where condition not max(condition in inner query)

I am trying to select all the rows where the userName and groupId is duplicated, and the userId is not the max userId for that userName/groupId combination. Here is my code so far:
select *
from userTable u
where exists
(select *
from userTable u1
where userName <> '' and userName is not null
and u.userName = u1.userName and u.groupId = u1.groupId
and u.userId <> max(u1.userId)
group by userName, groupId
having count(*) > 1)
order by userName
However, the line:
and u.userId <> u1.max(userId)
is giving me an error.
What is the right way to do this query?
SELECT u.*
FROM (
SELECT userName, groupId, MAX(userId) AS maxId
FROM userTable
GROUP BY
userName, groupId
HAVING COUNT(*) > 1
) q
JOIN userTable u
ON u.userName = q.userName
AND u.groupId = q.groupId
AND u.userId <> q.maxId
This should do it, I think:
select t.*
from dbo.UserTable t
join ( select userName , groupID , maxUserID = max(userID)
from dbo.UserTable x
group by userName , groupID
having count(*) > 1
) dupes on dupes.userName = t.userName
and dupes.groupID = t.groupID
and dupes.maxUserID > t.userID