UNION select sql statement returns duplicate results - sql-server

My objective is to have one record with both HomeNumber & OfficeNumber and I'm keeping the PersonID to reference the customer table. I'm thinking I shouldn't even be using a UNION but maybe a select query from two nested sub queries.
The results returned look like this.
1A370535-9432-45B9-8F08-004F040EE196 '' ''
1A370535-9432-45B9-8F08-004F040EE196 6127319561 ''
E8FA1667-416C-4639-ADDC-02143D651B4E '' 6512096719
E8FA1667-416C-4639-ADDC-02143D651B4E 6515786963 ''
Here my query:
SELECT PhoneNumbers.PersonID, PhoneNumbers.HomeNum, PhoneNumbers.OfficeNum
FROM (
SELECT PhoneHub.PersonID, ISNULL(PhoneHub.PhoneNbr, '') AS HomeNum, '' AS OfficeNum
FROM
--PhoneType INNER JOIN
PhoneHub --ON PhoneType.ID = PhoneHub.TypeID
WHERE
(PhoneHub.FranID = #FranID) AND
(PhoneHub.TypeID = '28321161-668e-4a56-90be-67a146fa1353') -- Home# ID
UNION
SELECT PhoneHub.PersonID, '' AS HomeNum, ISNULL(PhoneHub.PhoneNbr, '') AS OffNum
FROM
--PhoneType AS PhoneType_2 INNER JOIN
PhoneHub --AS PhoneHub ON PhoneType_2.ID = PhoneHub.TypeID
WHERE
(PhoneHub.FranID = #FranID) AND
(PhoneHub.TypeID = '02a4125b-b968-4dc6-9734-7f75f45f7635') --Office# ID
) AS PhoneNumbers
ORDER BY PhoneNumbers.PersonID
Table Schema - PhoneHub
PK ID uniqueidentifier
FK FranID uniqueidentifier Franchise.ID
FK PersonID uniqueidentifier Customer.ID
FK TypeID uniqueidentifier PhoneType.ID
PhoneNbr nvarchar(20)
PhoneExt nvarchar(10)
IsDefault bit

This looks like a pivot requirement.
SELECT PersonID,
Max(case when TypeID = '28321161-668e-4a56-90be-67a146fa1353'
then PhoneNbr End) HomeNum,
Max(case when TypeID = '02a4125b-b968-4dc6-9734-7f75f45f7635'
then PhoneNbr End) OfficeNum
FROM
PhoneHub
WHERE
PhoneHub.FranID = #FranID
GROUP BY PersonID

Select a.PersonID,Coalesce(home.PhoneNbr, '') AS HomeNum,Coalesce(office.PhoneNbr, '') AS OffNum from
(
SELECT PhoneHub.PersonID
FROM
PhoneHub
WHERE
(PhoneHub.FranID = #FranID) AND
(PhoneHub.TypeID = '28321161-668e-4a56-90be-67a146fa1353')
UNION
SELECT PhoneHub.PersonID
FROM
PhoneHub
WHERE
(PhoneHub.FranID = #FranID) AND
(PhoneHub.TypeID = '02a4125b-b968-4dc6-9734-7f75f45f7635')
) as a
left join PhoneHub home on (home.PersonID = a.PersonID) and (home.FranID = #FranID) AND (home.TypeID = '28321161-668e-4a56-90be-67a146fa1353')
left join PhoneHub office on (office.PersonID = a.PersonID) and (office.FranID = #FranID) AND (office.TypeID = '02a4125b-b968-4dc6-9734-7f75f45f7635')
order by a.PersonID

Related

Select all users from subgroups in T-SQL query [duplicate]

This question already has an answer here:
SQL Server recursive self join
(1 answer)
Closed 3 years ago.
I have a SQL Server table that holds groups and subgroups that I am using to define user permissions in application. What I am trying to accomplish is to select all users ids from all subgroups based on used id assigned to group.
I have tried with this query but it only shows one level of subgroup users.
SET #UserIds =
ISNULL(
STUFF((SELECT ',' + CAST(tbl.UserID AS VARCHAR(MAX))
FROM
(SELECT grpUser.UserId AS UserID
FROM dbo.GroupUser grpUser
INNER JOIN (SELECT subGrp.Id, subGrp.GroupName
FROM dbo.Groups grp
INNER JOIN dbo.GroupUser guser ON grp.Id = guser.GroupId
AND guser.UserId = #UserId
INNER JOIN dbo.Groups subGrp ON grp.Id = subGrp.ParentGroupId) tbl ON grpUser.GroupId = tbl.Id) AS tbl
FOR XML PATH('')), 1, 1, ''), '')
I am struggling to get users ids in all subgroups. Number of subgroups is not defined. Is there a way to do in sql or should I shift this part to application side? Thank you for any advice.
Finally I came up with this code. Thank you for hint.
declare #UserGroupId int;
Set #UserGroupId = (Select GroupId from GroupUser gu
left join dbo.Groups gr on gu.GroupId = gr.id
where UserId = #UserId and gr.IsDeleted = 0)
declare #UsersGroups Table (
Id int not null,
GroupName nvarchar(50) not null
)
;WITH grp AS
(
SELECT Id, GroupName
FROM Groups
WHERE ParentGroupId = 1
UNION ALL
SELECT g.Id, g.GroupName
FROM Groups g
INNER JOIN dbo.GroupUser guser ON g.Id = guser.GroupId
INNER JOIN grp ON g.ParentGroupId = grp.Id
)
Insert into #UsersGroups
SELECT Id, GroupName
FROM grp
SET #UserIds = ISNULL(STUFF(( SELECT ',' + CAST(tbl.UserID AS VARCHAR(max)) FROM (
SELECT grpUser.UserId AS UserID FROM dbo.GroupUser grpUser
INNER JOIN ( SELECT * FROM #UsersGroups
) tbl ON grpUser.GroupId = tbl.Id
) AS tbl FOR XML PATH('') ), 1, 1, ''), '')

SQL Server, How to create two select statement in one table

I make two select statements with different result sets,
Please help me to make those two select statements show the their result in one table,
DECLARE #EmpID INT
SELECT #EmpID = SubCategoryId
FROM dbo.Product
WHERE ProductId = 13
SELECT Product.ProductId
, Product.ProductName
, Product.ProductPrice
, Product.ProductQuantity
, Product.SubCategoryId AS ForUpdate
, SubCategory.SubCategoryName
, SubCategory.SubCategoryId
FROM Product
INNER JOIN ProductUnderCategory
ON ProductUnderCategory.ProductId = Product.ProductId
INNER JOIN SubCategory
ON ProductUnderCategory.SubCategoryId = SubCategory.SubCategoryId
WHERE Product.ProductId = 13
SELECT Property.Propertyid
, Property.PropertyName
, ProductProperties.PropertyValue
FROM Property
LEFT JOIN ProductProperties
ON Property.PropertyId = ProductProperties.PropertyId AND ProductProperties.ProductId = 13
WHERE Property.Propertyid IN (
SELECT PropertyId
FROM CategoryProperty
WHERE CategoryProperty.SubCategoryId = CategoryProperty.SubCategoryId AND CategoryProperty.SubCategoryId = #EmpID
)
I'm assuming you want the properties for each product (not a union, but a join). Something like this:
DECLARE #EmpID INT
SELECT #EmpID = SubCategoryId
FROM dbo.Product
WHERE ProductId = 13
SELECT Product.ProductId
, Product.ProductName
, Product.ProductPrice
, Product.ProductQuantity
, Product.SubCategoryId AS ForUpdate
, SubCategory.SubCategoryName
, SubCategory.SubCategoryId
, Property.Propertyid
, Property.PropertyName
, ProductProperties.PropertyValue
FROM Product
INNER JOIN ProductUnderCategory
ON ProductUnderCategory.ProductId = Product.ProductId
INNER JOIN SubCategory
ON ProductUnderCategory.SubCategoryId = SubCategory.SubCategoryId
LEFT JOIN ProductProperties
on ProductProperties.ProductID = Product.ProductID
left join Property
ON Property.PropertyId = ProductProperties.PropertyId
and Property.Propertyid IN (
SELECT PropertyId
FROM CategoryProperty
WHERE
CategoryProperty.SubCategoryId = CategoryProperty.SubCategoryId
AND CategoryProperty.SubCategoryId = #EmpID
)
WHERE Product.ProductId = 13
You probably can do something like the following - if a column doesn't exist in the 1st select you need to include whatever value is appropriate and must name the column. if a column doesn't exist in the subsequent selects then simply return null/''/0 or whatever you need as a value;
select Product.ProductId, Product.ProductName, Product.ProductPrice, Product.ProductQuantity, '' as someColumn, ....
from ....
union [all]
Select Property.Propertyid, Property.PropertyName, ProductProperties.PropertyValue, NULL (for ProductQuantity), ProductProperties.someColumn
from .....

SQL Server perfomance tuning - Union tables having 1.5 million rows of data

I have a stored procedure which unions two tables each with 1.5M rows of data. Created a fulltext index for the default search field (VARCHAR(255)) and a non-clustered index covering column names in select/where clause.
Also pagination is applied and fetching only the necessary columns from both
tables.
UNION is used as I want distinct records after combining the table.
The response time of the query varies from 3 seconds to more than a minute. When the filter text contains just two characters it is too slow, still fetches values faster when repeating the execution.
DECLARE #start INT, #length INT, #searchQuery VARCHAR(100) --Param
DECLARE #sortQuery VARCHAR(100)
SET #sortQuery = #searchQuery;
SET #searchQuery = ('"' + #searchQuery + '*"');
SELECT temp2.*
, t3.Name AS CountryName
, t5.Name AS CityName
, t4.Name AS StateProvinceName
, t4.Code AS StateProvinceCode
FROM
(SELECT temp1.* FROM
(SELECT ID
, Name
, ISNULL(Address1, '') AS AddressLine1
, ISNULL(Address2, '') AS AddressLine2
, ISNULL(CityID, 0) AS CityID
, ISNULL(CountryID, 0) AS CountryID
, ISNULL(StateProvinceID, 0) AS StateProvinceID
, ISNULL(PostalCode, '') AS PostalCode
FROM Table1
WHERE Deleted= 0 AND CONTAINS(Name, #searchQuery)
UNION
SELECT t2.EmployerID AS ID
, t2.DivisionName AS Name
, t2.Address1 AS AddressLine1
, t2.Address2 AS AddressLine2
, t2.CityID AS CityID
, t2.CountryID AS CountryID
, t2.StateProvinceID AS StateProvinceID
, t2.PostalCode AS PostalCode
FROM Table2 t2
JOIN Employer t1 ON t2.EmployerID = t1.ID
WHERE t2.Deleted= 0 AND t2.DivisionName IS NOT NULL AND t2.DivisionName <> ''
AND t1.Deleted = 0 AND t2.CountryID IS NOT NULL AND t2.Address1 IS NOT NULL AND t2.Address2 IS NOT NULL
AND CONTAINS(t2.DivisionName, #searchQuery)
) temp1
ORDER BY (CASE WHEN temp1.Name LIKE #sortQuery +'%' THEN 2 ELSE 1 END) DESC, temp1.Name
OFFSET #start ROWS
FETCH NEXT #length ROWS ONLY
) temp2
JOIN Table3 t3 ON C.ID = temp2.CountryID
JOIN Table4 t4 ON S.ID = temp2.StateProvinceID
JOIN Table5 t5 ON Ct.ID = temp2.CityID
ORDER BY temp2.Name

is this query optimized?

i got task to optimize the database, because this database has slow response.
is this possible, if i have around 90k records, if i want to select around 27k records, the responses time is 15s?
anything wrong with my query? or this is about indexes?
SET NOCOUNT ON;
SELECT workID AS pageID,
projectName AS recProject,
workType AS recType,
workTitle AS recTitle,
workDescription,
workDate AS recDate,
startTime,
endTime,
RTRIM(LTRIM(firstName + ' ' + lastName)) AS recName
FROM dbo.vTimesheets WITH (NOLOCK)
WHERE isActiveProject = 1
AND workType = 1
AND timeFor = 2
ORDER BY recdate DESC, pageID DESC;
SET NOCOUNT OFF;
this query for view
SELECT dbo.tblTimesheet.workID,
dbo.tblTimesheet.staffID,
dbo.tblTimesheet.workDate,
dbo.tblTimesheet.startTime,
dbo.tblTimesheet.endTime,
dbo.tblTimesheet.projectID,
dbo.tblTimesheet.timeFor,
dbo.tblTimesheet.workType,
dbo.tblTimesheet.workTitle,
dbo.tblTimesheet.workDescription,
dbo.tblProject.projectName,
dbo.tblProject.isDeleted AS isDeletedProject,
dbo.tblProject.isActive AS isActiveProject,
dbo.tblUser.firstName,
dbo.tblUser.lastName,
dbo.tblUser.isDeleted AS isDeletedStaff,
dbo.tblUser.staffType
FROM dbo.tblTimesheet WITH (NOLOCK)
INNER JOIN dbo.tblUser WITH (NOLOCK) ON dbo.tblTimesheet.staffID = dbo.tblUser.userID
INNER JOIN dbo.tblProject WITH (NOLOCK) ON dbo.tblTimesheet.projectID = dbo.tblProject.projectID
WHERE (dbo.tblTimesheet.isDeleted = 0)
ALTER TABLE dbo.tblTimesheet
ADD workDescription2 AS CAST(workDescription AS VARCHAR(MAX))
GO
CREATE INDEX ix ON dbo.tblTimesheet (projectID, staffID, workDate DESC, workID DESC)
INCLUDE (startTime, endTime, workType, workTitle, workDescription2, timeFor, isDeleted)
WHERE isDeleted = 0 AND workType = 1 AND timeFor = 2
--WITH (DROP_EXISTING=ON)
SELECT t.workID AS pageID,
t.workDate AS recDate,
t.startTime,
t.endTime,
t.workType AS recType,
t.workTitle AS recTitle,
t.workDescription2 AS workDescription,
p.projectName AS recProject,
RTRIM(LTRIM(u.firstName + ' ' + u.lastName)) AS recName
FROM dbo.tblTimesheet t --WITH (INDEX (ix))
JOIN dbo.tblUser u ON t.staffID = u.userID
JOIN dbo.tblProject p ON t.projectID = p.projectID
WHERE t.isDeleted = 0
AND p.isActive = 1
AND t.workType = 1
AND t.timeFor = 2
ORDER BY t.workDate DESC, t.workID DESC
this is the new execution plan
but this slower than before
CREATE INDEX ix1 ON dbo.tblTimesheet (projectID, staffID, workID)
INCLUDE (workType, timeFor, isDeleted)
WHERE isDeleted = 0 AND workType = 1 AND timeFor = 2
IF OBJECT_ID('tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp (
workID INT PRIMARY KEY,
recProject VARCHAR(50),
recName VARCHAR(150)
)
INSERT INTO #temp (workID, recProject, recName)
SELECT t.workID,
p.projectName,
RTRIM(LTRIM(u.firstName + ' ' + u.lastName)) AS recName
FROM dbo.tblTimesheet t WITH (INDEX (ix1))
JOIN dbo.tblUser u ON t.staffID = u.userID
JOIN dbo.tblProject p ON t.projectID = p.projectID
WHERE t.isDeleted = 0
AND p.isActive = 1
AND t.workType = 1
AND t.timeFor = 2
SELECT t.workID AS pageID,
t.workDate AS recDate,
t.startTime,
t.endTime,
1 AS recType,
t.workTitle AS recTitle,
t.workDescription,
tt.recProject,
tt.recName
FROM dbo.tblTimesheet t
JOIN #temp tt ON t.workID = tt.workID
ORDER BY t.workDate DESC, t.workID DESC
--OPTION(RECOMPILE)

Multiple COUNT(*) with join

I have to COUNT some rows from multiple tables. Before I can do multiple COUNT I will have to subselect. The problem here is that I need to JOIN some values in order to get the right result.
SELECT
sponsor.Name As SponsorName,
COUNT(participants.[Table]) AS ParticipantCount,
( SELECT
COUNT(guestcards.[Table])
FROM
guestcards
WHERE
guestcards.EventID = #EventID
AND
guestcards.[Table] = #Table
AND
guestcards.SponsorID = participants.SponsorID
-- Here lies the problem.
-- I will need to check up on another value to ensure I get the right rows, but participants.SponsorID is not here because of no join :-(
) AS GuestParticipantCount
FROM
participants
LEFT JOIN
sponsor
ON
sponsor.ID = participants.SponsorID
WHERE
participants.EventID = #EventID
AND
participants.[Table] = #Table
GROUP BY
sponsor.Name
Guestcards table holds: sponsorid, eventid, tablename
Participantstable holds: sponsorid, eventid, tablename
Sponsor table holds: id, name
I need to count how many "Participants" there are and how many "Guestcards" that in a particulary event. These participants have a table (where they should sit) and so does the guestcards. I need to check up on if its the same "table" where they sit.
So I need to count how many are sitting at table "A1" or table "A2" etc.
The result I am after is like:
"Sponsor Name has 5 participants and 3 guestcards. They sit on A1"
I hope I made my self clear
Here's exact equivalent of you query (grouping on sponsor.Name):
SELECT sponsor.name,
COALESCE(SUM(participantCount), 0),
COALESCE(SUM(guestcardsCount), 0)
FROM (
SELECT sponsorId, COUNT(*) AS participantCount
FROM participants
WHERE eventId = #eventId
AND [table] = #table
GROUP BY
sponsorId
) p
FULL JOIN
(
SELECT sponsorId, COUNT(*) AS guestcardsCount
FROM guestdcards
WHERE eventId = #eventId
AND [table] = #table
GROUP BY
sponsorId
) g
ON g.sponsorId = p.sponsorId
FULL JOIN
sponsor s
ON s.id = COALESCE(p.sponsorId, g.sponsorId)
GROUP BY
s.sponsorName
However, I believe you want something more simple:
SELECT sponsorName, participantCount, guestcardsCount
FROM sponsor s
CROSS APLLY
(
SELECT COUNT(*) AS participantCount
FROM participants
WHERE sponsorId = s.id
AND eventId = #eventId
AND [table] = #table
) p
CROSS APLLY
(
SELECT COUNT(*) AS guestcardsCount
FROM guestdcards
WHERE sponsorId = s.id
AND eventId = #eventId
AND [table] = #table
) g
Update:
SELECT sponsor.name,
COALESCE(participantCount, 0),
COALESCE(guestcardsCount, 0)
FROM (
SELECT sponsorId, COUNT(*) AS participantCount
FROM participants
WHERE eventId = #eventId
AND [table] = #table
GROUP BY
sponsorId
) p
FULL JOIN
(
SELECT sponsorId, COUNT(*) AS guestcardsCount
FROM guestdcards
WHERE eventId = #eventId
AND [table] = #table
GROUP BY
sponsorId
) g
ON g.sponsorId = p.sponsorId
JOIN sponsor s
ON s.id = COALESCE(p.sponsorId, g.sponsorId)

Resources