Can someone please have a look into query and suggest any improvement or optimized query for the same so that query runs faster .
So basically, I have 2 table Survey and SurveyInvite.
Sample data for Table Survey
CREATE TABLE dbo.Survey
(
createdate date,
emailinvite char(4),
phoneinvite char(4),
smsinvite char(4),
surveyid int
);
INSERT dbo.Survey VALUES
('20220201','12ab','12bc', null ,1),
('20220210','23be','45hg','45tr',2),
('20220220','65hg', null ,'89kj',3);
Sample data for Table SurveyInvite
CREATE TABLE dbo.SurveyInvite
(
sentdate date,
id char(4)
);
INSERT dbo.SurveyInvite VALUES
('20220201','12ab'),
('20220205','12bc'),
('20220210','23be'),
('20220214','45hg'),
('20220218','45tr'),
('20220220','65hg'),
('20220224','89kj');
The output should be
Type | sentdate | inviteid | surveyid |
---|---|---|---|
2022-02-01 | 12ab | 1 | |
2022-02-10 | 23be | 2 | |
2022-02-20 | 65hg | 3 | |
Phone | 2022-02-05 | 12bc | 1 |
Phone | 2022-02-14 | 45hg | 2 |
SMS | 2022-02-18 | 45tr | 2 |
SMS | 2022-02-24 | 89kj | 3 |
So basically, I have to get sentdate from SurveyInvite table against each type(email,phone,sms).
Survey table should be unpivoted on email,phone and sms to transform column into rows.
Here's my query
SELECT 'Email' as Type,esi.sentdate,emailinvite as inviteid,s.surveyid
FROM Survey s
INNER JOIN SurveyInvite esi on s.emailinvite=esi.id
UNION
SELECT 'SMS' as Type,ssi.sentdate,smsinvite as inviteid,s.surveyid
FROM Survey s
INNER JOIN SurveyInvite ssi on s.smsinvite=ssi.id
UNION
SELECT 'Phone' as Type,psi.sentdate,phoneinvite as inviteid,s.surveyid
FROM Survey s
INNER JOIN SurveyInvite psi on s.phoneinvite=psi.id
Please suggest other way to write query if that makes query faster. I am still trying using UNPIVOT,left join,CTE to avoid using UNION.
CodePudding user response:
You don't need to query the tables three times, you can just unpivot. The easiest way to do this is with a CROSS APPLY (VALUES
SELECT
v.Type,
ssi.sentdate,
v.inviteid,
s.surveyid
FROM Survey s
CROSS APPLY (VALUES
('Email', s.emailinvite),
('Phone', s.phoneinvite),
('SMS', s.smsinvite)
) v (Type, inviteid)
INNER JOIN SurveyInvite ssi on v.inviteid = ssi.id;
I suggest you consider normalizing your database in the first place by storing the data unpivoted in a separate table.
CodePudding user response:
I assume that you have set the primary and foreign keys correctly. It might also beneficial to have indexes on the foreign keys. See: Should every SQL Server foreign key have a matching index?.
As always with these performance questions. Only benchmarking different variants can tell you which one is the fastest. The same query can perform very differently with a different set of data.
One possibility is to use joins and base the query on SurveyInvite
:
SELECT
I.sentdate,
CASE WHEN SE.id IS NOT NULL THEN 'Email'
WHEN SP.id IS NOT NULL THEN 'Phone'
ELSE 'SMS'
END AS Type,
I.id AS inviteid,
CASE WHEN SE.id IS NOT NULL THEN SE.surveyid
WHEN SP.id IS NOT NULL THEN SP.surveyid
ELSE SS.surveyid
END AS surveyid
FROM
SurveyInvite I
LEFT JOIN Survey SE
ON I.emailinvite = SE.id
LEFT JOIN Survey SP
ON I.phoneinvite = SP.id
LEFT JOIN Survey SS
ON I.smsinvite = SS.id
CodePudding user response:
Another way (again the key is to only read either table once instead of three times):
SELECT i.sentdate,
[Type] = REPLACE(u.Types, 'invite', ''),
inviteid = u.id,
u.surveyid
FROM dbo.Survey AS s
UNPIVOT (Id FOR Types IN
(emailinvite, phoneinvite, smsinvite)) AS u
INNER JOIN dbo.SurveyInvite AS i ON u.Id = i.id;
As you can see from the db<>fiddle, this eliminates 4 of the 6 table scans and also an expensive distinct sort.