How to Optimize this SQL Query? - sql

I have 3 tables:
CRSTasks (ID,parentID)
CRSTaskReceivers (ID,tskID,receiverID)
UserNames (id,name)
...relation between CRSTasks and CRSTaskReceivers one-to-many
between UserNames and CRSTaskReceivers one-to-one
tasks
ID parent
1 null
10 1
50 1
taskReceivers
id taskID receiverID
1 1 4(john)
1 10 2(mike)
1 50 3(brand)
I need result like that:
taskid Receivers
-------------------
1 jone,mike,brand
ONLY FOR PARENT TASKS IT WILL CONCATE RECEIVERS

SQL Server 2005+:
SELECT t.id AS taskid,
STUFF((SELECT ','+ x.name
FROM (SELECT COALESCE(pu.[ArabicName], aut.Name) AS name
FROM CRSTaskReceivers tr
JOIN AD_USER_TBL aut ON aut.id = tr.receiverid
LEFT JOIN PORTAL_USERS pu ON pu.id = aut.id
WHERE tr.crstaskid = t.id
AND tr.receivertype = 1
UNION
SELECT agt.name
FROM CRSTaskReceiver tr
JOIN AD_GROUP_TBL sgt ON agt.id = tr.receiverid
WHERE tr.receivertype = 3
AND tr.crstaskid = t.id) x
FOR XML PATH('')), 1, 1, '')
FROM CRSTasks t
Don't need the function.

Besides the odd string concatenation going on it sure looks like all that could be done in one query instead of four. It's perfectly fine to have more than one criteria in a join. Something along:
FROM CRSTaskReceiver
INNER JOIN CRSTask
ON CRSTaskReceiver.CRSTaskID = CRSTask.ID
INNER JOIN CRS_BuiltinGroup
ON CRSTaskReceiver.ReceiverID = CRS_BuiltinGroup.ID AND CRSTaskReceiver.ReceiverType = 4
WHERE CRSTask.ParentTask = #TaskID
Also the below part of the function seems to do absolutely nothing. What is it meant to do?
DECLARE #tmpLength INT
SET #tmpLength = 0
SET #tmpLength = LEN(#tmp)
IF #tmpLength > 0
BEGIN
SET #tmp = SUBSTRING(#tmp, 0, #tmpLength)
END

Related

Update Column in table using Select, Inner Join and Case When

I have a table called: Claim, where I've already moved data from another table called Damage. However, today I needed to add a column in Claim called ClaimStatusID. And to give values to ClaimStatusID I need to to it based on another column called DamageApprovedStatusID, which exists in yet another table called DamageErrand. Damage and DamageErrand has relation through a crosstable called DamageErrandCrossDamage. In that crosstable DamageErrandID and DamageID exists. Here, I've commented more information:
-- Claim has column ID which has the same ID as the table Damage
-- DamageErrand has DamageApprovedStatus ID which need to be inserted
-- to ClaimStatusID, where the ID's need to be correct.
-- Table Damage and Table DamageErrand has a cross table:
-- DamageErrandCrossDamage, where both of their ID's are stored
-- Claim.ID should therefore be the same as DamageErrandCrossDamage.DamageID
-- Since Claim.ID has the same ID as Damage.ID
-- IF DamageApprovedStatus = -1, SET ClaimStatusID = 0
-- IF DamageApprovedStatus = 0, SET ClaimStatusID = 4
-- IF DamageApprovedStatus = 1, SET ClaimStatusID = 2
And here's a SQL query I created for selecting and using Case When to give the correct statuses:
SELECT Claim.ID as claimid, DamageErrandID as damageerrandeid,
CASE --
WHEN DamageErrand.DamageApprovedStatusID = -1 THEN 0
WHEN DamageErrand.DamageApprovedStatusID = 0 THEN 4
WHEN DamageErrand.DamageApprovedStatusID = 1 THEN 2
ELSE '-'
END AS DamageApprovedStatusID,
DamageApprovedStatusID
FROM
DamageErrand
INNER JOIN DamageErrandCrossDamage ON DamageErrand.ID =
DamageErrandCrossDamage.DamageErrandID
INNER JOIN Claim ON DamageErrandCrossDamage.DamageID = Claim.ID
WHERE Claim.ID = DamageErrandCrossDamage.DamageID
I do believe this is correct, with the ID's matching and everything. But how can I actually update the table Claim with this? I know insert won't work since I have columns that does not allow null in Claim table. I tried doing something similar to:
--UPDATE Claim
--SET ClaimStatusID =
--(SELECT DamageApprovedStatusID FROM
--DamageErrand
--INNER JOIN DamageErrandCrossDamage ON DamageErrand.ID =
-- DamageErrandCrossDamage.DamageErrandID
--INNER JOIN Claim ON DamageErrandCrossDamage.DamageID = Claim.ID
--WHERE Claim.ID = DamageErrandCrossDamage.DamageID)
But obviously this won't work either. Really thankful for any help!
Btw: I'm using SQL-server for this.
I believe something like this should work:
UPDATE Claim
SET ClaimStatusID = alias.DamageApprovedStatusID
FROM (
SELECT DamageErrandCrossDamage.DamageID,
CASE
WHEN DamageErrand.DamageApprovedStatusID = -1 THEN 0
WHEN DamageErrand.DamageApprovedStatusID = 0 THEN 4
WHEN DamageErrand.DamageApprovedStatusID = 1 THEN 2
ELSE '-'
END AS DamageApprovedStatusID
FROM DamageErrand
INNER JOIN DamageErrandCrossDamage ON DamageErrand.ID = DamageErrandCrossDamage.DamageErrandID
) alias
WHERE Claim.ID = alias.DamageID
I guess it could be rewritten like this too:
UPDATE Claim
SET ClaimStatusID = CASE
WHEN DamageErrand.DamageApprovedStatusID = -1 THEN 0
WHEN DamageErrand.DamageApprovedStatusID = 0 THEN 4
WHEN DamageErrand.DamageApprovedStatusID = 1 THEN 2
ELSE '-'
END
FROM DamageErrand
INNER JOIN DamageErrandCrossDamage ON DamageErrand.ID = DamageErrandCrossDamage.DamageErrandID
INNER JOIN Claim ON DamageErrandCrossDamage.DamageID = Claim.ID

how do I loop through a set of records

how do I loop through a set of records from a select?
Here is my stored procedure query:
ALTER PROCEDURE [dbo].[procedure_rastavljanjeOtpremnica] (#radniNalogID INT) AS
DECLARE #bool BIT = 0
IF(#bool = 'FALSE')
BEGIN
SELECT ROW_NUMBER() OVER(ORDER BY S.PuniNaziv) AS num_row, S.PuniNaziv, a.KolicinaMjeracaIElektronike FROM ArtikliUslugeNaloga a
INNER JOIN SifreArtikala S ON S.Id = a.Artikal1Id
INNER JOIN UslugeNaloga ON UslugeNaloga.Id = a.UslugeNalogaId
WHERE a.RobaSkart = #bool AND UslugeNaloga.RadniNalogID = #radniNalogID
END
The result is:
now_row PuniNaziv Kolicina
1 Komunik internet jed s ugrađ WIFi sensoNET 3
2 Ventil tahret DN15 (1/2") 5
3 Vodokotlić Geberit 1
So what I need to do is to add another column that will print KOM for every row,
This is the result that I am looking for:
now_row PuniNaziv Kolicina Jedinia mjere
1 Komunik internet jed s ugrađ WIFi sensoNET 3 Kom
2 Ventil tahret DN15 (1/2") 5 Kom
3 Vodokotlić Geberit 1 Kom
Can anyone give me idea how to do this
There is no need to "loop". Just add the column to your query:
SELECT ROW_NUMBER() OVER (ORDER BY S.PuniNaziv) AS num_row,
S.PuniNaziv, a.KolicinaMjeracaIElektronike,
'Kom' as Jedinia_mjere
FROM ArtikliUslugeNaloga a JOIN
SifreArtikala S
ON S.Id = a.Artikal1Id JOIN
UslugeNaloga
ON UslugeNaloga.Id = a.UslugeNalogaId
WHERE a.RobaSkart = #bool AND
UslugeNaloga.RadniNalogID = #radniNalogID;
Note: If you want to ensure that the rows are returned in the order specified by num_row, you should add ORDER BY num_row to the query.

Get the sum of a count column in SQL

I have the following query
SELECT
dtc.coupon_type_company_name,
COUNT(*) * dtc.coupon_type_company_coupon_amount AS 'Total_Coupon_To_Be_Used',
dtc.coupon_type_company_coupon_months_combinable
FROM
[dbo].[coupon_type_Company_User] dtcu
JOIN
coupon_type_Company dtc ON dtcu.coupon_type_Company_ID = dtc.id
JOIN
person p ON dtcu.userID = p.userID
WHERE
coupon_type_company_coupon_is_combinable = 1
OR coupon_type_company_has_coupon = 1
AND dtc.companyID = 1081
AND p.is_active = 1
GROUP BY
dtc.coupon_type_company_name,dtc.coupon_type_company_coupon_amount,
dtc.coupon_type_company_coupon_months_combinable
This returns the following:
What I want to have however is just one column and one row that should take the SUM of my middle column (count(*)*dtc.coupon_type_company_coupon_amount).
How could I achieve this and prevent doing this in my code backend (C#)
You can wrap your query like this:
SELECT SUM(Total_Coupon_To_Be_Used) AS the_sum
FROM (
your query
) s
Use a "Table Expression", as in:
select sum(Total_Coupon_To_Be_Used) from (
SELECT dtc.coupon_type_company_name,
count(*) * dtc.coupon_type_company_coupon_amount as 'Total_Coupon_To_Be_Used',
dtc.coupon_type_company_coupon_months_combinable
FROM [dbo].[coupon_type_Company_User] dtcu
JOIN coupon_type_Company dtc ON dtcu.coupon_type_Company_ID = dtc.id
JOIN person p ON dtcu.userID = p.userID
WHERE coupon_type_company_coupon_is_combinable = 1
or coupon_type_company_has_coupon = 1
and dtc.companyID = 1081
AND p.is_active = 1
GROUP BY
dtc.coupon_type_company_name,dtc.coupon_type_company_coupon_amount,
dtc.coupon_type_company_coupon_months_combinable
) x

Query table with all names in one statement

I have a table that I cannot alter and I am trying to wrap my head around an appropriate way to query this table in one statement based on one given value.
Roughly here's what the table looks like:
What I'm after is a way to query the table using only the SubBody code in order to get all names associated with it.
for example,
<query> where SubBody = '1001'
returns
| HName | HSName | BName | BSName |
+-----------------------------------+
| Toys | Sport | Ball | Baseball |
SELECT Head.Name as HName ,
SubHead.Name as HSName ,
Body.Name as BName ,
SubBody.Name as BSName
FROM yourTable as SubBody
JOIN yourTable as Body
ON SubBody.Body = Body.Body
AND Body.SubBody IS NULL
JOIN yourTable as SubHead
ON Body.SubHead = SubHead.SubHead
AND SubHead.Body IS NULL
JOIN yourTable as Head
ON SubHead.Head = Head.Head
AND Head.SubHead IS NULL
WHERE SubBody.SubBody = '1001'
Although you can express the logic as joins, for some reason correlated subqueries come to mind first:
select (select th.name from t th where th.head = t.head and th.subhead is null) as hname,
(select ts.name from t ts where ts.head = t.head and ts.subhead = t.subhead and t.body is null) as sname,
(select tb.name from t tb where tb.head = t.head and tb.subhead = t.subhead and tb.body = t.body and tb.subbody is null) as bname,
t.name as bsname
from t
where t.subbody = 1001
This answer is close to what Juan Carlos has posted. Which one you like depends a bit on your style.
WITH BaseRecord AS(
SELECT *
FROM ToyTable
WHERE SubBody = '1001'
)
SELECT
h.Name AS HName,
hs.Name AS HSName,
b.Name AS BName,
bs.Name AS BSName
FROM BaseRecord bs
INNER JOIN ToyTable b ON bs.Body = b.Body AND ISNULL(b.SubBody,'') = ''
INNER JOIN ToyTable hs ON bs.SubHead = hs.SubHead AND ISNULL(hs.Body,'') = ''
INNER JOIN ToyTable h ON bs.Head = h.Head AND ISNULL(h.SubHead,'') = ''
I'm not sure whether your "empty" cells contain nulls or empty strings. Either way, both are taken into account here.

Sql how to change query based on parameters

Sorry I don't know how to put it in title, I have this query on an aspx page with 2 datepickers (let's say data and data2)
select utenti.nome as tecnico, Richieste.IDRic as idchia, richieste.descr as rdescr, ISNULL(richieste.assistremota,0) as assremota, Clienti.RagSociale as ragsoc, richieste.descr as descr, richieste.priorita as prior, richieste.tipo as tipo, richieste.rforologio as rforo, ISNULL(statoric.appuntamento,0) as app, ISNULL(statoric.oradalle,0) as dalle, ISNULL(statoric.oraalle,0) as alle, statoric.ID as idstato
from clienti
inner join richieste on clienti.idcliente = richieste.rfcliente
inner join statoric on statoric.rfric = richieste.idric
inner join stati on stati.idstato = statoric.rfstato
inner join utenti on utenti.idutente=statoric.rftecnico
where statoric.attuale = 1 and statoric.rfstato < 14 and statoric.dataass = #data and statoric.rftecnico = 8 order by app desc, oraalle asc, prior desc
I need to change the "statoric.dataass = #data" part to do this (pseudocode):
if data 2 is null then
"statoric.dataass = #data"
else
"statoric.dataass between #data and #data2"
end if
How can I do it? I tried case and If but I'm doing something wrong... thanks
Try coalesce
... statoric.dataass between #data and coalesce(#data2, #data) ...
Here is one method:
where statoric.attuale = 1 and
statoric.rfstato < 14 and
( (#data2 is null and statoric.dataass = #data) or
statoric.dataass between #data and #data2
) and
statoric.rftecnico = 8
However, you might consider this:
where statoric.attuale = 1 and
statoric.rfstato < 14 and
statoric.dataass >= #data and
statoric.dataass <= coalesce(#data2, #data) and
statoric.rftecnico = 8
The advantage of this approach is that it can take advantage of an index on (attuale, rftecnico, dataass).