Duplicate value in XMLAGG and RTRIM SQL Query - sql

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

Related

CTE values need to insert into another table

Below query i am not able to insert common table expression select values into another table.
;WITH cte AS
(
SELECT
rd.reorderhistoryId,
rd.dateCreated,
rd.personId,
rd.reorderId,
rd.statusNo,
rd.createdBy,
r.OrganizationId
FROM
(SELECT
MIN(reorderhistoryId) AS reorderhistoryId,
MIN(personId) AS personId,
reorderId
FROM
reorderHistoryDetails
GROUP BY
reorderId) rh
INNER JOIN
reorderHistoryDetails rd ON rd.reorderhistoryId = rh.reorderhistoryId
INNER JOIN
reorderDetails r ON r.personId = rd.personId
WHERE
rd.statusNo = 1059
)
SELECT DISTINCT TOP 5
rlst.personId AS personId,
'Start Reorder Process' AS reorderStatusType,
1 AS productId,
3 AS amountPaid,
rlst.reorderId,
rlst.OrganizationId AS orgId,
rlst.createdBy,
rlst.dateCreated,
rlst.dateCreated AS timeStamp
FROM
cte rlst
INNER JOIN
doctororderall d ON rlst.personId = d.personId
AND d.productId = 1
AND YEAR(rlst.dateCreated) = YEAR(d.dateCreated)
INNER JOIN
patientdetails pd ON pd.personId = d.personId
WHERE
rlst.datecreated > '2020-09-01'
AND rlst.dateCreated <= '2021-10-06'
INSERT INTO rouletteTracking (personId, reorderStatusType, productId,
amountPaid, reorderId, orgId,
createdBy, dateCreated, timeStamp)
SELECT *
FROM cte
I'm getting this error:
Msg 208, Level 16, State 1, Line 14
Invalid object name 'cte'.
Both myself (in the comments) and Grant (in their answer) has already covered this, however, to repeat this a Common Table Expression is an expression. It's scope is limited to the scope of the statement it is define in (like other expressions). If you properly terminate your statements you'll see you have two statements above; a SELECT and an INSERT, and thus the CTE isn't defined in the second:
WITH cte AS
(SELECT rd.reorderhistoryId,
rd.dateCreated,
rd.personId,
rd.reorderId,
rd.statusNo,
rd.createdBy,
r.OrganizationId
FROM (SELECT MIN(reorderhistoryId) AS reorderhistoryId,
MIN(personId) AS personId,
reorderId
FROM reorderHistoryDetails
GROUP BY reorderId) rh --on d.personId=rh.personId
INNER JOIN reorderHistoryDetails rd ON rd.reorderhistoryId = rh.reorderhistoryId
INNER JOIN reorderDetails r ON r.personId = rd.personId
WHERE rd.statusNo = 1059)
SELECT DISTINCT TOP 5
rlst.personId AS personId,
'Start Reorder Process' AS reorderStatusType,
1 AS productId,
3 AS amountPaid,
rlst.reorderId,
rlst.OrganizationId AS orgId,
rlst.createdBy,
rlst.dateCreated,
rlst.dateCreated AS timeStamp
FROM cte rlst
INNER JOIN doctororderall d ON rlst.personId = d.personId
AND d.productId = 1
AND YEAR(rlst.dateCreated) = YEAR(d.dateCreated)
INNER JOIN patientdetails pd ON pd.personId = d.personId
WHERE rlst.datecreated > '2020-09-01'
AND rlst.dateCreated <= '2021-10-06'; --This statement ends HERE
--New statement starts here. The CTE cte has no context here.
INSERT INTO rouletteTracking (personId,
reorderStatusType,
productId,
amountPaid,
reorderId,
orgId,
createdBy,
dateCreated,
timeStamp)
SELECT *
FROM cte;
Assuming you want to SELECT the TOP (5) (arbitrary) rows first from the CTE first, and then INSERT the entire result set from the CTE into your table afterwards, I would INSERT the data into a temporary table first, and then SELECT and INSERT from that:
WITH cte AS
(SELECT rd.reorderhistoryId,
rd.dateCreated,
rd.personId,
rd.reorderId,
rd.statusNo,
rd.createdBy,
r.OrganizationId
FROM (SELECT MIN(reorderhistoryId) AS reorderhistoryId,
MIN(personId) AS personId,
reorderId
FROM reorderHistoryDetails
GROUP BY reorderId) rh --on d.personId=rh.personId
INNER JOIN reorderHistoryDetails rd ON rd.reorderhistoryId = rh.reorderhistoryId
INNER JOIN reorderDetails r ON r.personId = rd.personId
WHERE rd.statusNo = 1059)
SELECT *
INTO #Temp
FROM cte;
SELECT DISTINCT TOP 5
rlst.personId AS personId,
'Start Reorder Process' AS reorderStatusType,
1 AS productId,
3 AS amountPaid,
rlst.reorderId,
rlst.OrganizationId AS orgId,
rlst.createdBy,
rlst.dateCreated,
rlst.dateCreated AS timeStamp
FROM #Temp rlst
INNER JOIN doctororderall d ON rlst.personId = d.personId
AND d.productId = 1
AND YEAR(rlst.dateCreated) = YEAR(d.dateCreated)
INNER JOIN patientdetails pd ON pd.personId = d.personId
WHERE rlst.datecreated > '2020-09-01'
AND rlst.dateCreated <= '2021-10-06'
ORDER BY {The Column to order by}; --This statement ends HERE
--New statement starts here. The CTE cte has no context here.
INSERT INTO rouletteTracking (personId,
reorderStatusType,
productId,
amountPaid,
reorderId,
orgId,
createdBy,
dateCreated,
timeStamp)
SELECT *
FROM #Temp;
Note: I expect the INSERT statement at the end to still fail. You attempt to insert into a timestamp column (a deprecated synonym for rowversion); that isn't allowed.
The name Common Table Expression is a bit misleading. Many people read it and see the word "TABLE" very prominently. They then assume that they're dealing with a new kind of temporary table or table variable. However, the word to focus on is "EXPRESSION".
A CTE is only useable within the statement that defines it. It's not, in any way, temporary storage. It's simply a query. It's like having a sub-query to define a table like this:
SELECT *
FROM (SELECT * FROM dbo.MyTable) as NotaCTEButActsLikeOne
In order to do your process, you need to move the entire SELECT statement including your CTE into a single statement with the INSERT process.
EDIT:
Here. You just move everything so that it's a single statement:
WITH
cte AS
(SELECT rd.reorderhistoryId,
rd.dateCreated,
rd.personId,
rd.reorderId,
rd.statusNo,
rd.createdBy,
r.OrganizationId
FROM (SELECT MIN(reorderhistoryId) AS reorderhistoryId,
MIN(personId) AS personId,
reorderId
FROM reorderHistoryDetails
GROUP BY reorderId) rh --on d.personId=rh.personId
INNER JOIN reorderHistoryDetails rd ON rd.reorderhistoryId = rh.reorderhistoryId
INNER JOIN reorderDetails r ON r.personId = rd.personId
WHERE rd.statusNo = 1059)
INSERT INTO rouletteTracking (personId,
reorderStatusType,
productId,
amountPaid,
reorderId,
orgId,
createdBy,
dateCreated,
timeStamp)
SELECT DISTINCT TOP 5
rlst.personId AS personId,
'Start Reorder Process' AS reorderStatusType,
1 AS productId,
3 AS amountPaid,
rlst.reorderId,
rlst.OrganizationId AS orgId,
rlst.createdBy,
rlst.dateCreated,
rlst.dateCreated AS timeStamp
FROM cte rlst
INNER JOIN doctororderall d ON rlst.personId = d.personId
AND d.productId = 1
AND YEAR(rlst.dateCreated) = YEAR(d.dateCreated)
INNER JOIN patientdetails pd ON pd.personId = d.personId
--inner join (select min(reorderhistoryId) as reorderhistoryId,min(personId) as personId,reorderId from reorderHistoryDetails where statusNo=1059 and personId=226020 group by reorderId )as rh on d.personId=rh.personId
--inner join reorderHistoryDetails rd on rd.reorderHistoryId=rh.reorderhistoryId and rd.statusNo=1059
WHERE rlst.datecreated > '2020-09-01'
AND rlst.dateCreated <= '2021-10-06';
Also, please, the semi-colon is a statement terminator, not something you put in front of the WITH clause. Examples on line do that so that people can copy the code and it will compile (a righteous approach). However, just putting it at the end of your statements, everything will work and the code won't look awful.

DISTINCT return same ID two times wrongly

This is my SQL query:
SELECT DISTINCT(ItemId), TCode, PartNumber,ModelNumber, ItemUOM
FROM #Results
This query returns:
ItemId TCode Source PartNumber ModelNumber ItemUOM
-----------------------------------------------------------------
1024 1000 NULL NULL EA
1024 1000 FLEX FLEX EA
#Result is a temp table I have used left join in that query
Why does SELECT DISTINCT return the same ItemID 1024 twice?
SELECT DISTCINT(I.ItemId),
(DENSE_RANK() OVER(ORDER BY I.ItemId ASC)) AS RowNumber,
(I.TCode), E.Name AS Source,
I.GoldenRecordNumber AS GoldenRecordNo, I.ItemCode AS MMRefNo,
I.ShortDescription AS ShortText, I.LongDescription AS POText,
Suppliers.Description AS Manufacturer, Suppliers.Name AS ManufacturerCode,
Suppliers.Abbreviation AS ManufacturerAbbr,
ItemSuppliers.ReferenceNo AS PartNumber, ItemSuppliers.ReferenceNo AS ModelNumber,
UOM.Name AS ItemUOM, MG.Name AS PSGC,
NM.Noun AS ClassName, NM.LongAbbrevation AS ClassDescription
INTO
#Results
FROM
Items I
LEFT JOIN
ItemSuppliers ON I.ItemId = ItemSuppliers.ItemsId
LEFT JOIN
Suppliers ON ItemSuppliers.ManufacturerId = Suppliers.SupplierId
LEFT JOIN
UnitOfMeasurement UOM ON UOM.UOMId = I.UOMId
LEFT JOIN
MaterialGroup MG ON MG.MaterialGroupId = I.MaterialGroupId
LEFT JOIN
NounModifiers NM ON NM.NounModifierId = I.NounModifierId
LEFT JOIN
AutoClass AC ON AC.ClassName = NM.Noun
LEFT JOIN
ERP E ON E.ERPId = I.ERPName
LEFT JOIN
NounModifierAttributes NMA ON NMA.NounModifierId =
NM.NounModifierId
LEFT JOIN
Attributes A ON A.AttributeId = NMA.AttributeId
LEFT JOIN
ItemAttributes IA ON IA.ItemId = I.ItemId
WHERE
(I.ItemCode LIKE '%'+'2001010088'+'%' )
SELECT 'Int' = COUNT(distinct(ItemId))
FROM #Results
WHERE (TCode IS NOT NULL OR MMRefNo IS NOT NULL)
SELECT DISTINCT(ItemId),
TCode, Source, GoldenRecordNo, MMRefNo, ShortText, POText,
Manufacturer, ManufacturerCode, ManufacturerAbbr, PartNumber, ModelNumber,
ItemUOM, PSGC, ClassName, ClassDescription
FROM
#Results
WHERE
(TCode IS NOT NULL OR MMRefNo IS NOT NULL)
AND RowNumber BETWEEN (1-1)*100 + 1 AND (((1-1) * 100 + 1) + 100) - 1
DROP TABLE #Results
if you are convinced the rows which are selected can be grouped together then it should work fine.
1. but if rows are having different data then distinct will not help.
2. use ltrim,rtrim to remove leading and trailing spaces.
example: distinct(ltrim(rtrim(ItemId)))
this will help if it due to spaces or for junk values
The behavior of DISTINCT works as expected. For instance, you could use GROUP BY clause to group them by ItemId, TCode to get top most records
SELECT
ItemId, TCode,
MAX(PartNumber) PartNumber, MAX(ModelNumber) ModelNumber,
MAX(ItemUOM), ...
FROM #Results
GROUP BY ItemId, TCode
In case any failure in GROUP BY clause use ranking function to assign the rank and get the record based on rank value.

query issue with a LEFT JOIN

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

how to get the count in SQL Server?

I have tried a lot to figure how to get the count from two tables with respect to master table
I have three tables
Using these table values I need to get this output..
Tried but could get the desired result
http://en.wikipedia.org/wiki/Join_(SQL)
SQL - LEFT OUTER JOIN and WHERE clause
http://forums.devshed.com/oracle-development-96/combination-of-left-outer-join-and-where-clause-383248.html
You have to first GROUP BY in subqueries, then JOIN to the main table:
SELECT
a.AttributeId
, COALECSE(cntE, 0) AS cntE
, COALECSE(cntM, 0) AS cntM
FROM
AttributeMaster AS a
LEFT JOIN
( SELECT
AttributeId
, COUNT(*) AS cntE
FROM
EmployeeMaster
GROUP BY
AttributeId
) em
ON em.AttributeId = a.AttributeId
LEFT JOIN
( SELECT
AttributeId
, COUNT(*) AS cntM
FROM
MonthlyDerivedMaster
GROUP BY
AttributeId
) mdm
ON mdm.AttributeId = a.AttributeId
SELECT AttributeId,
(SELECT COUNT(Eid) FROM EmployeeMaster WHERE AttributeMaster.AttributeId = EmployeeMaster.AttributeId) as master_eid,
(SELECT COUNT(Eid) FROM MonthnlyDerivedMaster WHERE AttributeMaster.AttributeId = MonthnlyDerivedMaster.AttributeId) as monthly_eid
FROM AttributeMaster

SQL Server 2005 - Row_Number()

I'm trying to understand the unusual behaviour seen when ordering results in a descending order using the row_number() function when using a DISITINCT on the outermost select in my query as below:
SELECT DISTINCT (ID), State_Id, Name_Of_Trip, Date_Of_Travel, Creation_Date, Locking_Id, Applicant_Name, Reference_Number, State_Name
FROM (
SELECT app.ID, app.State_Id, app.Name_Of_Trip, app.Date_Of_Travel, app.Creation_Date, app.Locking_Id, app.Applicant_Name, app.Reference_Number,
State.Name AS State_Name, ROW_NUMBER() OVER(ORDER BY Reference_Number DESC) as rowNum
FROM Application_Leg AS app
INNER JOIN State AS state
ON app.State_Id = state.ID
WHERE (app.State_Id = 5 OR app.State_Id = 6 OR app.State_Id = 8) AND app.Organisation_Id=12
AND Leg_Number IN
(SELECT DISTINCT Leg_Number
from Application_Leg as al
INNER JOIN
Organisation as org
ON al.Organisation_Id = org.ID
WHERE al.ID=app.ID AND org.Approval_Required=1 AND Mode_Of_Transport=1))
as pagedApplications
WHERE rowNum BETWEEN 0 AND (0 + 10)
When the outermost DISTINCT is taken out then the descending order is fine but when it is included the results are not shown in descending order.
ORDER BY in ROW_NUMBER clause does not guarantee the order of the resultset.
ROW_NUMBER usually uses sorting in the query plan which results in the fact that the values come out presorted.
This is a side effect and should not be relied upon.
DISTINCT uses Hash Match (Aggregate) which breaks sorting.
Add ORDER BY clause to the end of the query:
SELECT DISTINCT (ID), State_Id, Name_Of_Trip, Date_Of_Travel, Creation_Date, Locking_Id, Applicant_Name, Reference_Number, State_Name
FROM (
SELECT app.ID, app.State_Id, app.Name_Of_Trip, app.Date_Of_Travel,
app.Creation_Date, app.Locking_Id, app.Applicant_Name, app.Reference_Number,
State.Name AS State_Name, ROW_NUMBER() OVER(ORDER BY Reference_Number DESC) as rowNum
FROM Application_Leg AS app
INNER JOIN
State AS state
ON app.State_Id = state.ID
WHERE app.State_Id IN (5, 6, 8)
AND app.Organisation_Id = 12
AND Leg_Number IN
(
SELECT Leg_Number
FROM Application_Leg as al
INNER JOIN
Organisation as org
ON al.Organisation_Id = org.ID
WHERE al.ID = app.ID
AND org.Approval_Required = 1
AND Mode_Of_Transport = 1
)
) AS pagedApplications
WHERE rowNum BETWEEN 0 AND (0 + 10)
ORDER BY
ReferenceNumber DESC
Also note that it will not return 10 distinct results, it will return DISTINCT of the first 10 results.
If you want the former, use this:
SELECT DISTINCT TOP 10 ID, State_Id, Name_Of_Trip, Date_Of_Travel, Creation_Date, Locking_Id, Applicant_Name, Reference_Number, State_Name
FROM (
SELECT app.ID, app.State_Id, app.Name_Of_Trip, app.Date_Of_Travel,
app.Creation_Date, app.Locking_Id, app.Applicant_Name, app.Reference_Number,
State.Name AS State_Name
FROM Application_Leg AS app
INNER JOIN
State AS state
ON app.State_Id = state.ID
WHERE app.State_Id IN (5, 6, 8)
AND app.Organisation_Id = 12
AND EXISTS
(
SELECT Leg_Number
FROM Application_Leg AS al
INNER JOIN
Organisation as org
ON al.Organisation_Id = org.ID
WHERE al.ID = app.ID
AND al.LegNumber = app.LegNumber
AND org.Approval_Required = 1
AND Mode_Of_Transport = 1
)
) AS pagedApplications
ORDER BY
ReferenceNumber DESC
Have you tried adding an order by to your outer select?