Concatenating row values using Inner Join - sql

I have this query that I'm using to join two tables for an update statement. This is the query that I built:
DECLARE #DocHoldReasons VARCHAR(8000)
SET #DocHoldReasons = 'DocType Hold'
UPDATE dbo.EpnPackages
SET Error = 1, Msg = COALESCE (#DocHoldReasons + ': ', '') + cv.Value
FROM EpnPackages p
INNER JOIN EpnCountyValues cv ON cv.CountyId = p.CountyId and cv.ValueName = 'DocHoldReason'
WHERE p.Status = 1000
AND p.Error = 0
There are two rows in the EpnCountyValues table with the same ValueName, and I need them both concatenated, and I'm having a tough time doing it. All I can get is the first row value. This is what my resulting string should look like - 'DocType Hold: Test: Test.124.'
eidt: I need the rows with the same ValueName to be concatenated for the update query. There could be more than two rows with ValueName = DocHoldReason
Here's the structure of the EpnCountyValues table:
CountyValueId CountyId ValueName Value
1 1 DocHoldReason Test
2 2 xyz Test1
3 3 DocHoldReason Test.124.
Any help would be greatly appreciated. Thanks!

You can do what you want by pre-aggregating the table before the join. If there are only two values and you don't care about the order, then this will work:
DECLARE #DocHoldReasons VARCHAR(8000);
SET #DocHoldReasons = 'DocType Hold';
UPDATE dbo.EpnPackages
SET Error = 1,
Msg = (COALESCE(#DocHoldReasons + ': ', '') + minv +
(case when minv <> maxv then ': ' + maxv else '' end)
)
FROM EpnPackages p INNER JOIN
(select cv.CountyId, min(cv.value) as minv, max(cv.value) as maxv
from EpnCountyValues cv
where cv.ValueName = 'DocHoldReason'
) cv
ON cv.CountyId = p.CountyId
WHERE p.Status = 1000 AND p.Error = 0;
EDIT:
For more than two values, you have to do string concatenation. That is "unpleasant" in SQL Server. Here is the approach:
DECLARE #DocHoldReasons VARCHAR(8000);
SET #DocHoldReasons = 'DocType Hold';
UPDATE dbo.EpnPackages
SET Error = 1,
Msg = (COALESCE(#DocHoldReasons + ': ', '') +
stuff((select ': ' + cv.value
from EpnCountyValues cv
where cv.ValueName = 'DocHoldReason' and
cv.CountyId = p.CountyId
for xml path ('')
), 1, 2, '')
)
WHERE p.Status = 1000 AND p.Error = 0;
This version does it using a correlated subquery rather than a join with an aggregation.
EDIT II:
You can fix this with an additional coalesce:
DECLARE #DocHoldReasons VARCHAR(8000);
SET #DocHoldReasons = 'DocType Hold';
UPDATE dbo.EpnPackages
SET Error = 1,
Msg = (COALESCE(#DocHoldReasons + ': ', '') +
COALESCE(stuff((select ': ' + cv.value
from EpnCountyValues cv
where cv.ValueName = 'DocHoldReason' and
cv.CountyId = p.CountyId
for xml path ('')
), 1, 2, ''), '')
)
WHERE p.Status = 1000 AND p.Error = 0;

You can join the EpnCountyValues table twice!
Something like this,
DECLARE #DocHoldReasons VARCHAR(8000)
SET #DocHoldReasons = 'DocType Hold'
Select #DocHoldReasons + ': ' + ev1.Value + ': ' + ev2.Value
From EpnPackages p
Join EpnCountyValues ev1 on p.packageID = ev1.packageID
Join EpnCountyValues ev2 on ev1.ValueName = ev2.ValueName
Where ev1.ValueName = 'DocHoldRegion'
And p.status = 1000
And p.error = 0
Does that select look right to you? If so, run the update. I always try to run a select before an update. I have to admit, I'm a bit confused why you're joining on CountyID, but then it seems like you'll be updating the values from different counties?

Related

Why query takes more time to execute when I use if else statement in stored procedure?

I have used below query in my stored procedure. When I am using if else statement in the stored procedure query takes more time to execute. When I remove if else statement query takes less time to return result.
If #P_Make = 'ALL' Or IsNull(#P_Make, '') = ''
Begin
Set #P_Make = '%';
End
Else
Begin
Set #P_Make = #P_Make + '%';
End
If #P_Model = 'ALL' Or IsNull(#P_Model, '') = ''
Begin
Set #P_Model = '%';
End
Else
Begin
Set #P_Model = #P_Model + '%';
End
If #P_Location = 'ALL' Or IsNull(#P_Location, '') = ''
Begin
Set #P_Location = '%';
End
Else
Begin
Set #P_Location = #P_Location + '%';
End
If #P_City = 'ALL' Or IsNull(#P_City, '') = ''
Begin
Set #P_City = '%';
End
Else
Begin
Set #P_City = #P_City + '%';
End
If #P_Category = 'ALL' Or IsNull(#P_Category, '') = ''
Begin
Set #P_Category = '%';
End
Else
Begin
Set #P_Category = Case When #P_Category = 'Bikes & Scooters' Then '2W%'
When #P_Category = '3 Wheelers' Then '3W%'
When #P_Category = 'Cars & SUVs' Then '4W%'
When #P_Category = 'Trucks' Then 'CV%'
When #P_Category = 'Farm Equipments' Then 'FE%'
When #P_Category = 'Industrial Equipments' Then 'IE%'
When #P_Category = 'Construction Equipments' Then 'CE%'
Else #P_Category + '%'
End
End
If #P_Service = 'ALL' Or IsNull(#P_Service, '') = ''
Begin
Set #P_Service = ''
End
Select Count(C.Sal_Pk_Id)
From dbo.Auction (NoLock) A
Inner Join dbo.PLACE (NoLock) B On A.Auc_Place_Fk_Id = B.Place_Pk_Id
Inner Join dbo.SALES_DETAILS (NoLock) C On A.Auc_Code = C.Sal_Auc_Code
Inner Join dbo.AUCTIONSERVICES (NoLock) D On C.Sal_Regno = D.Auc_Service_Regno And C.Sal_Auc_Code = D.Auc_Service_Auctioncode
Inner Join dbo.LOT (NoLock) E On C.Sal_Lot_No = E.Lot_Lot_No And C.Sal_Auc_Code = E.Lot_Auc_Code
Inner Join dbo.INVENTORY (NoLock) F On C.Sal_Regno = F.Inv_H_Reg_No
Where C.[Status] = 'L' And E.Lot_Sold_Status = 'S'
And IsNull(B.Place_City, '') Like #P_City And IsNull(B.Place_State, '') Like #P_Location
And IsNull(F.Inv_H_Category, '') Like #P_Category And IsNull(F.Inv_H_Mfg_Name, '') Like #P_Make
And IsNull(F.Inv_H_Model, '') Like #P_Model
Query without if else statement:
Select Count(C.Sal_Pk_Id)
From dbo.Auction (NoLock) A
Inner Join dbo.PLACE (NoLock) B On A.Auc_Place_Fk_Id = B.Place_Pk_Id
Inner Join dbo.SALES_DETAILS (NoLock) C On A.Auc_Code = C.Sal_Auc_Code
Inner Join dbo.AUCTIONSERVICES (NoLock) D On C.Sal_Regno = D.Auc_Service_Regno And C.Sal_Auc_Code = D.Auc_Service_Auctioncode
Inner Join dbo.LOT (NoLock) E On C.Sal_Lot_No = E.Lot_Lot_No And C.Sal_Auc_Code = E.Lot_Auc_Code
Inner Join dbo.INVENTORY (NoLock) F On C.Sal_Regno = F.Inv_H_Reg_No
Where C.[Status] = 'L' And E.Lot_Sold_Status = 'S'
And (#P_City = '' Or IsNull(B.Place_City, '') = #P_City)
And (#P_Location= '' Or IsNull(B.Place_State, '') = #P_Location)
And (#P_Category = '' Or IsNull(F.Inv_H_Category, '') = #P_Category)
And (#P_Make = '' Or IsNull(F.Inv_H_Mfg_Name, '') = #P_Make)
And (#P_Model = '' Or IsNull(F.Inv_H_Model, '') = #P_Model)
Why the query output takes less time when I remove if else statement in the stored procedure?
There's two main reasons - like and execution plan.
like '%something' is the slowest possible thing you can filter by - it means going row by row, reading the whole data in the column and doing a comparison. It can't use any index seeks, only scans.
Second, you only get one execution plan, based on the first way the procedure is called. This pretty much guarantees that for any other input data, the performance will suffer. In your second example, while the plan is again sub-optimal and dependent on the initial inputs, it doesn't completely ignore all the possible filters - it just optimizes based on statistics.
Dynamic filters aren't something nobody tried to solve before. Learn from the best: http://www.sommarskog.se/dyn-search.html
I think the query without if else is faster because it does not have a Like operator
Where C.[Status] = 'L' And E.Lot_Sold_Status = 'S'
And IsNull(B.Place_City, '') Like #P_City And IsNull(B.Place_State, '') Like #P_Location
And IsNull(F.Inv_H_Category, '') Like #P_Category And IsNull(F.Inv_H_Mfg_Name, '') Like #P_Make
And IsNull(F.Inv_H_Model, '') Like #P_Model
and I suppose that the second query can use indexes more efficiently. The first and second queries are different, please look at Actual Execution Plan and you will realize why it happens.

Concatenating 2 columns in sql and applying like operation on the result

SELECT
CONCAT(SG.firstname +' ', SG.lastName) AS 'Name',
SG.entityId 'ID',
dbo.GetTitleDescriptionByEntityID(SG.entityId) 'Title',
CASE ISNULL(EC.address2, '')
WHEN '' THEN EC.address1
ELSE EC.address1 +', ' + EC.address2
END +', ' + EC.city + ', ' + LDState.ItemName + ', ' + EC.zip 'Address',
CR.clinicalRate
FROM
StaffGeneral SG WITH (NOLOCK)
JOIN
LookupDetails LD WITH (NOLOCK) ON SG.employeeStatus = LD.lookupDetailsId AND LD.ItemAbbreviation = 'SCSSC'
JOIN
othersRating or1 WITH (nolock) ON SG.entityId = or1.entityId AND dateRated = (SELECT TOP 1 MAX(dateRated) FROM OthersRating r1 WITH (nolock) WHERE r1.entityid = SG.entityId)
JOIN
LookupDetails LDRating with (nolock) ON or1.rating = LDRating.lookupDetailsId AND LDRating.ItemAbbreviation NOT IN ('RTNP', 'RTDNU', 'RTPENDING', 'RTDELETE', 'RTTERMI')
JOIN
EntityContacts EC with (nolock) ON SG.entityId = EC.entityId AND (eC.contactId = 'General' or EC.contactId = 'General1') AND EC.deleted = 0
JOIN
LookupDetails LDState WITH (NOLOCK) ON EC.state = LDState.lookupDetailsId
JOIN
ContractorRate CR ON SG.entityId = CR.entityId AND CR.deleted = 0
WHERE
SG.isActive = 0 AND SG.deleted = 0
AND ((SG.entityId = #subContractorID) OR (#subContractorID IS null))
AND ((#subContractorName IS NULL) OR ((EC.firstName + ' ' + EC.lastName) LIKE #subContractorName + '%'))
In the above query I wanted to concat the first and last name with space and query it against the input to the stored procedure #subcontractorname.
When the subcontractorname is null that is fine but I am not able to get results when it jumps to Or clause .
Tried the SQL Server Profiler but does not show anything how the query is formed
I tried executing the SQL by putting the whole SQL in a var but I am hitting the problem of varchar(max).
Please advise

SQL Error The multi-part identifier could not be bound

When I'm trying to execute this query, I get "The multi-part identifier "#EachEmployee.ResultID" could not be bound." error.
DECLARE #QueryText VARCHAR(1000)
SET #QueryText = '
UPDATE #EachEmployee2
SET #EachEmployee2.CorrectAnswerCount = (
SELECT COUNT (TMID)
FROM
' + #WorkDatabaseName + '.dbo.TestBlockTM AS TBTM,
' + #WorkDatabaseName + '.dbo.TestResultTM AS TRTM
WHERE 1 = 1
AND TBTM.TMID = TRTM.OtvetID
AND TBTM.TMPID = TRTM.VoprosID
AND TRTM.TestResultID = #EachEmployee2.ResultID
)
WHERE
#EachEmployee2.IsGroup = 0 AND #EachEmployee2.BlockID = 1'
EXECUTE(#QueryText)
However, the similar query is working perfectly:
UPDATE #EachEmployee2
SET #EachEmployee2.ResultID = (
SELECT TOP 1 TestResultID
FROM #AnswersList AS a
WHERE 1 = 1
AND a.SID = #EachEmployee2.SID
AND a.UserID = #EachEmployee2.UserID
)
Can someone tell what's a problem here? Thanks and appreciate your help.
try this
SET #QueryText = '
UPDATE emp
SET emp.CorrectAnswerCount = (
SELECT COUNT (TMID)
FROM
' + #WorkDatabaseName + '.dbo.TestBlockTM AS TBTM,
' + #WorkDatabaseName + '.dbo.TestResultTM AS TRTM
WHERE 1 = 1
AND TBTM.TMID = TRTM.OtvetID
AND TBTM.TMPID = TRTM.VoprosID
AND TRTM.TestResultID = emp.ResultID
)
From #EachEmployee2 emp
WHERE
emp.IsGroup = 0 AND emp.BlockID = 1'
Can you try this query and tell me if the error still occurs:
SET #QueryText = '
UPDATE #EachEmployee2
SET CorrectAnswerCount = (
SELECT COUNT (TMID)
FROM
' + #WorkDatabaseName + '.dbo.TestBlockTM AS TBTM,
INNER JOIN ' + #WorkDatabaseName + '.dbo.TestResultTM AS TRTM ON TRTM.OtvetID = TBTM.TMID
AND TRTM.VoprosID = TBTM.TMPID
AND TRTM.TestResultID = E.ResultID
)
FROM #EachEmployee2 E
WHERE E.IsGroup = 0
AND E.BlockID = 1'
I added the FROM clause and modified a bit the subquery in order to replace the old SQL syntax by an explicit INNER JOIN clause.
Hope this will help

Need to speed up this SQL query

My query has this structure:
SELECT DISTINCT (CO.CateringOrderId),
CO.CateringOrderNumber,
MC.FirstName + ' ' + MC.LastName AS "CustomerName",
CO.EventDate AS EventDate,
CO.IsCompleted,
CO.IsVerified,
MC.EmailId,
CAT.OfficePhone,
CAT.Mobile,
CAT.Fax,
CO.TotalInvoiceAmount,
CO.BarterCharityId,
(SELECT Sum (Amount)
FROM Catering_Order_Payment_Trans
WHERE CateringOrderId = CO.CateringOrderId) AS AmountReceived
FROM Catering_Orders CO,
Master_Customer MC,
Customer_Address_Trans CAT,
Catering_Order_Employee_Trans COET
WHERE MC.CompanyId = #p_CompanyId
AND (MC.CustomerId = #p_CustomerId OR #p_CustomerId = -1)
AND (CO.CateringOrderNumber LIKE '%' + #p_CateringOrderNumber + '%')
AND (CO.EventDate >= CONVERT (DATETIME, #p_FromDate) OR #p_FromDate = '')
AND (CO.EventDate <= CONVERT (DATETIME, #p_ToDate) OR #p_ToDate = '')
AND (CO.IsCompleted = #p_IsCompleted OR #p_IsCompleted = -1)
AND (COET.EmployeeId = #p_CatererId OR #p_CatererId = -1)
AND MC.CustomerId = CO.CustomerId
AND MC.PersonalAddressId = CAT.CustomerAddressId
AND (COET.CateringOrderId = CO.CateringOrderId
OR CO.CateringOrderId NOT IN
(SELECT CateringOrderId FROM Catering_Order_Employee_Trans))
AND (CAT.Mobile like '%' + #p_ContactNumber + '%' )
AND (CO.IsActive is null or CO.IsActive=1)
ORDER BY CO.CateringOrderId DESC
I think the SUM sub-query is slowing it. Please suggest me on how to speed it up.
Currently its execution time is around 7 - 10 seconds.
Try something like this -
SELECT DISTINCT
CO.CateringOrderId,
CO.CateringOrderNumber,
MC.FirstName + ' ' + MC.LastName AS CustomerName,
CO.EventDate AS EventDate,
CO.IsCompleted,
CO.IsVerified,
MC.EmailId,
CAT.OfficePhone,
CAT.Mobile,
CAT.Fax,
CO.TotalInvoiceAmount,
CO.BarterCharityId,
AmountReceived = (
SELECT SUM(t.Amount)
FROM dbo.Catering_Order_Payment_Trans t
WHERE t.CateringOrderId = CO.CateringOrderId
)
FROM (
SELECT *
FROM dbo.Catering_Orders
WHERE ISNULL(IsActive, 1) = 1
AND (IsCompleted = #p_IsCompleted OR #p_IsCompleted = -1)
AND CateringOrderNumber LIKE '%' + #p_CateringOrderNumber + '%'
AND EventDate BETWEEN
CONVERT(DATETIME, ISNULL(NULLIF(#p_FromDate, ''), '18000101'))
AND
CONVERT(DATETIME, ISNULL(NULLIF(#p_ToDate, ''), '30000101'))
) CO
JOIN dbo.Master_Customer MC ON MC.CustomerId = CO.CustomerId
JOIN dbo.Customer_Address_Trans CAT ON MC.PersonalAddressId = CAT.CustomerAddressId
LEFT JOIN (
SELECT *
FROM dbo.Catering_Order_Employee_Trans
WHERE EmployeeId = #p_CatererId
OR #p_CatererId = -1
) COET ON COET.CateringOrderId = CO.CateringOrderId
WHERE MC.CompanyId = #p_CompanyId
AND (MC.CustomerId = #p_CustomerId OR #p_CustomerId = -1)
AND CAT.Mobile LIKE '%' + #p_ContactNumber + '%'
The main problems in
AND (COET.CateringOrderId = CO.CateringOrderId
OR CO.CateringOrderId NOT IN
(SELECT CateringOrderId FROM Catering_Order_Employee_Trans))
and
(SELECT Sum (Amount)
FROM Catering_Order_Payment_Trans
WHERE CateringOrderId = CO.CateringOrderId) AS AmountReceived
Try:
SELECT CO.CateringOrderId,
CO.CateringOrderNumber,
MC.FirstName + ' ' + MC.LastName AS "CustomerName",
CO.EventDate AS EventDate,
CO.IsCompleted,
CO.IsVerified,
MC.EmailId,
CAT.OfficePhone,
CAT.Mobile,
CAT.Fax,
CO.TotalInvoiceAmount,
CO.BarterCharityId,
COPT.AmountReceived
FROM Catering_Orders CO
JOIN Master_Customer MC ON MC.CustomerId = CO.CustomerId
JOIN Customer_Address_Trans CAT ON MC.PersonalAddressId = CAT.CustomerAddressId
LEFT JOIN (SELECT CateringOrderId, Sum(Amount) AS AmountReceived
FROM Catering_Order_Payment_Trans
GROUP BY CateringOrderId) COPT
ON COPT.CateringOrderId = CO.CateringOrderId
WHERE MC.CompanyId = #p_CompanyId
AND (MC.CustomerId = #p_CustomerId OR #p_CustomerId = -1)
AND (CO.CateringOrderNumber LIKE '%' + #p_CateringOrderNumber + '%')
AND (CO.EventDate >= CONVERT (DATETIME, #p_FromDate) OR #p_FromDate = '')
AND (CO.EventDate <= CONVERT (DATETIME, #p_ToDate) OR #p_ToDate = '')
AND (CO.IsCompleted = #p_IsCompleted OR #p_IsCompleted = -1)
AND EXISTS
(SELECT NULL
FROM Catering_Order_Employee_Trans COET
WHERE COET.CateringOrderId = CO.CateringOrderId AND
(COET.EmployeeId = #p_CatererId OR #p_CatererId = -1) )
AND (CAT.Mobile like '%' + #p_ContactNumber + '%' )
AND (CO.IsActive is null or CO.IsActive=1)
ORDER BY CO.CateringOrderId DESC
To optimize your query you should edit the OR condition inserted inside AND conditions which really slow down your query
AND ( CO.EventDate <= CONVERT (DATETIME, #p_ToDate)
OR #p_ToDate = '')
should become something like
AND ( CO.EventDate <= CASE WHEN #p_ToDate = '' THEN
GETDATE()
ELSE
CONVERT (DATETIME,#p_ToDate)
END)
Also try to remove one by one the innested select both in select and in where to see which one most slows down the query

Stored Procedure with dynamic SQL returns wrong data

For the fiscal year parameter I have 2010, 2011 and 2012 as an option but no matter what year I select the "submitted On" column still shows 2011 dates. I want for example if I select 2010, only data submitted on 2010 should show and likewise for all the years. Any help?
set #sqlstr = '
Select
[SchoolName] As [School Name],
Status= CASE WHEN PS.PrivateSchoolID = Ed.PrivateSchoolID THEN ''Submitted''
ELSE ''Not Submitted''END,
[Submitted By]= CASE WHEN PS.PrivateSchoolID = Ed.PrivateSchoolID
THEN [FirstName] + '' '' + [LastName] ELSE NULL END,
[Submitted On]= CASE WHEN PS.PrivateSchoolID = Ed.PrivateSchoolID
THEN Convert( Varchar(10), Ed.CreatedDate, 101 ) ELSE NULL END
From EnrollmentDateSchool Ed Right Outer Join
(select FP.FiscalYear, PrivateSchool.* from PrivateSchool
INNER JOIN FiscalYearPrivateSchool FP
ON PrivateSchool.PrivateSchoolID = FP.PrivateSchoolID) PS
ON Ed.PrivateSchoolID = PS.PrivateSchoolID Left Outer Join
Finance.dbo.Person P ON Ed.CreatedBy = P.PersonID
Where (FiscalYear = '+convert(varchar, #FiscalYear)+') AND (PS.IsActive = 1)'
IF (#SchoolID != -1)
set #sqlstr = #sqlstr + ' AND SchoolID ='+ convert(varchar, #SchoolID)
IF (#Status = -1)
set #sqlstr = #sqlstr + ' AND (PS.PrivateSchoolID = PS.PrivateSchoolID' + ')'
ELSE IF (#Status = 1)
set #sqlstr = #sqlstr + ' AND (PS.PrivateSchoolID = Ed.PrivateSchoolID' + ')'
ELSE
set #sqlstr = #sqlstr + 'AND (Ed.PrivateSchoolID is null' + ')'
SET #sqlstr = #sqlstr + 'ORDER BY SchoolName'
EXEC(#sqlstr)
END
There is no need at all for dynamic sql here.
SELECT
...
FROM ...
WHERE FiscalYear=#FiscalYear AND PS.IsActive=1
AND (#SchoolID = -1 OR SchoolID=#SchoolID)
AND ( (#Status = -1)
OR (#Status=1 AND PS.PrivateSchoolID = Ed.PrivateSchoolID)
OR (Ed.PrivateSchoolID is null) )
ORDER BY SchoolName