The multi-part identifier "[column name]" could not be bound in UPDATE of TEMP Table - sql-server

I am trying to create a stored procedure whereupon I input a (simple for now) query into a temp table, and then replace some of the data with data from a different table based on a key.
Here is the complete code:
CREATE PROCEDURE GetInquiryList
AS
BEGIN
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#Inq ') IS NOT NULL
DROP TABLE #Inq
SELECT i.*,q.QuoteID INTO #Inq FROM Inquiries i left join Quotes q on i.InquiryId = q.InquiryId
WHERE i.YNDeleted = 0
--SELECT * FROM #Inq
UPDATE #Inq
SET j.InquiryCustomerName = c.CustomerName,
j.InquiryCustomerEmail = c.CustomerEmail,
j.InquiryCustomerPhone = c.CustomerPhone1,
j.InquiryBestTimetoCall = c.CustomerBestTimetoCall,
j.InquiryDay = c.customerDay,
j.InquiryNight = c.CustomerNight
SELECT c.CustomerName,
c.CustomerEmail,
c.CustomerPhone1,
c.CustomerBestTimetoCall,
c.customerDay,
c.CustomerNight
FROM Customers c
INNER JOIN #Inq j ON
j.InquiryCustomerID = c.CustomerID
SELECT * FROM #Inq
END
I get the following error:
Msg 4104, Level 16, State 1, Line 15 The multi-part identifier "j.InquiryCustomerName" could not be bound
I get this error for whatever column is placed first after the SET command.
Both query pieces of this work independently (the first select creating the temp table and the joined query at the bottom). The data returned is correct. I have tried using aliases (SELECT c.CustomerName AS Name, ...).
Originally, I used "#Inq i" in the second command, but changed to "j" out of an abundance of caution.
I have also run the command against the original table (substituting the Inquiry table for the temp table #Inq, and that fails as well).
Shortening it to this:
UPDATE #Inq
SET j.InquiryCustomerName = c.CustomerName,
j.InquiryCustomerEmail = c.CustomerEmail,
j.InquiryCustomerPhone = c.CustomerPhone1,
j.InquiryBestTimetoCall = c.CustomerBestTimetoCall,
j.InquiryDay = c.customerDay,
j.InquiryNight = c.CustomerNight
FROM Customers c
INNER JOIN #Inq j ON
j.InquiryCustomerID = c.CustomerID
I get a different error:
Msg 4104, Level 16, State 1, Line 15 The multi-part identifier "j.InquiryCustomerName" could not be bound
I'm sure it's probably something simple,(so simple that I can't find any references in any of my searches).
I'm sure it has something to do with the fact that you can't update the same instance of the table used in the join (I'm going to have to re-join again with a "k" alias). How do I do this?
data from the first query
data from the first query
data from the second select statement on the actual temp table
Here is what I updated the stored procedure to, which works exactly how I need it to:
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#Inq ') IS NOT NULL
DROP TABLE #Inq
SELECT i.* INTO #Inq FROM (
select inquiries.InquiryId,
inquiries.InquiryDateReceived,
inquiries.InquiryCustomerID,
cust.CustomerName as InquiryCustomerName,
cust.CustomerEmail as InquiryCustomerEmail,
cust.CustomerPhone1 as InquiryCustomerPhone,
cust.CustomerBestTimeToCall as InquiryBestTimeToCall,
cust.CustomerDay as InquiryDay,
cust.CustomerNight as InquiryNight,
inquiries.InquiryServiceType,
inquiries.InquiryServiceID,
inquiries.InquiryTimeframe,
inquiries.InquiryProjectDescription,
inquiries.InquiryDateResponded,
inquiries.InquiryCustomerReply,
inquiries.YNMigrated,
inquiries.InquiryDateClosed,
inquiries.YNClosed,
inquiries.YNDeleted
from inquiries inner join dbo.Customers as cust
on inquiries.InquiryCustomerID = cust.CustomerID and inquiries.InquiryCustomerID > 0
UNION ALL
select inquiries.InquiryId,
inquiries.InquiryDateReceived,
inquiries.InquiryCustomerID,
InquiryCustomerName,
InquiryCustomerEmail,
InquiryCustomerPhone,
InquiryBestTimeToCall,
InquiryDay,
InquiryNight,
inquiries.InquiryServiceType,
inquiries.InquiryServiceID,
inquiries.InquiryTimeframe,
inquiries.InquiryProjectDescription,
inquiries.InquiryDateResponded,
inquiries.InquiryCustomerReply,
inquiries.YNMigrated,
inquiries.InquiryDateClosed,
inquiries.YNClosed,
inquiries.YNDeleted
from inquiries WHERE inquiries.InquiryCustomerID = 0
) i
select i.*, q.QuoteID
FROM #Inq i left join dbo.Quotes as q
on i.InquiryId = q.InquiryId
WHERE i.YNDeleted = 0
END

Just stop using this pattern without a really good reason. Here it only appears to create more work for the database engine with no obvious benefit. Your procedure - as posted - has trivially simple queries so why bother with the temp table and the update?
It is also time to start learning and using best practices. Terminate EVERY statement - eventually it will be required. Does order of the rows in your resultset matter? Usually it does and that is only guaranteed when that resultset is produced by a query that includes an ORDER BY clause.
As a developing/debugging short cut, you can harness the power of CTEs to help you build a working query. In this case, you can "stuff" your first query into a CTE and then simply join the CTE to Customers and "adjust" the columns you need in that resultset.
WITH inquiries as (
select inq.*, qt.QuoteID
FROM dbo.Inquiries as inq left join dbo.Quotes as qt
on inq.InquiryId = qt.InquiryId
WHERE inq.YNDeleted = 0
)
select inquiries.<col>,
...,
cust.CustomerName as "InquiryCustomerName",
...
from inquiries inner (? guessing) dbo.Customers as cust
on inquiries.InquiryCustomerID = cust.CustomerID
order by ...
;
Schema names added as best practice. Listing the columns you actually need in your resultset is another best practice. Note I did not do that for the query in the CTE but you should. You can choose to create aliases for your resultset columns as needed. I listed one example that corresponds to your UPDATE attempt.
It is odd and very suspicious that all of the columns you intended to UPDATE exist in the Inquiries table. Are you certain you need to do that at all? Do they actually differ from the related columns in the Customer table? Also odd that the value 0 exists in InquiryCustomerID - suggesting you might have not a FK to enforce the relationship. Perhaps that means you need to outer join rather than inner join (as I wrote). If an outer join is needed, then you will need to use CASE expressions to "choose" which value (the CTE value or the Customer value) to use for those columns.

After learning a lot more about how things get bound to models, and how to further use sql, here is what my stored procedure looks like:
ALTER PROCEDURE [dbo].[GetInquiryList]
#InquiryID int = 0
AS
BEGIN
SET NOCOUNT ON
select i.InquiryId,
i.InquiryDateReceived,
i.InquiryCustomerID,
InquiryCustomerName =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryCustomerName
ELSE c.CustomerName
END,
InquiryCustomerEmail =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryCustomerEmail
ELSE c.CustomerEmail
END,
InquiryCustomerPhone =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryCustomerPhone
ELSE c.CustomerPhone1
END,
InquiryBestTimetoCall =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryBestTimetoCall
ELSE c.CustomerBestTimetoCall
END,
InquiryDay =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryDay
ELSE c.CustomerDay
END,
InquiryNight =
CASE i.InquiryCustomerID
WHEN 0 THEN i.InquiryNight
ELSE c.CustomerNight
END,
i.InquiryServiceType,
i.InquiryServiceID,
i.InquiryTimeframe,
i.InquiryProjectDescription,
i.InquiryDateResponded,
i.InquiryCustomerReply,
i.YNMigrated,
i.InquiryDateClosed,
i.YNClosed,
i.YNDeleted, ISNULL(q.QuoteId,0) AS Quoteid
FROM dbo.Inquiries i
LEFT JOIN dbo.Quotes q ON i.InquiryId = q.InquiryId
LEFT JOIN dbo.Customers c ON i.InquiryCustomerID = c.CustomerId
WHERE i.YNDeleted = 0
END
I'm sure there are additional enhancements that could be made, but avoiding the union is a big savings. Thanks, everyone.

Related

Find all lines where a value exists in one line

Here is my query
select order_no, pkg_no, zone_no
from T_DETAIL_ITEM a
where order_no = 495
order by order_no, pkg_no
For a given package I have zone number = 0
What I need to do is return all the lines with the pkg_no = 1597. Because one where exists with a zero zone.
I tried a few different 'where exists' lines and it isn't working.
Try to self join.
This way you can put your requirement in the second table "instance" but retrieve everything from that table matches based on another common field.
select distinct a.order_no, a.pkg_no, a.zone_no
from T_DETAIL_ITEM a
join T_DETAIL_ITEM b on b.pkg_no = a.pkg_no
where b.zone_no = 0
order by a.order_no, a.pkg_no
The accepted answer is good, but I had saw that you noted you tried EXISTS, so I wrote this example up using that method.
SELECT *
FROM T_Detail_Item d
WHERE exists (SELECT * FROM T_Detail_Item dx WHERE dx.pkg_no = d.pkg_no AND dx.zone_no = 0)
One way is to self reference the table in a left join and only include those with a zone_no=0, within the join clause. The filter out non-matching records by excluding records that do not match from the left join, T2.pkg_no = NULL.
SELECT
T1.pkg_no, T1.zone_no
FROM
T_DETAIL_ITEM T1
LEFT OUTER JOIN T_DETAIL_ITEM T2 ON T2.pkg_no = T1.pkg_no AND T2.zone_no = 0
WHERE
NOT T2.pkg_no IS NULL
If I correctly understood the question, you need something like this
BEGIN
declare #str varchar(max);
set #str='';
SELECT #str=#str+ [YOURCOLUMB]
FROM [YOURTABLE]
SELECT #str
END

SQL - (Update, From) statement with inner join on the same table

I have the below SQL query, the purpose of this query to detect a missing sequence: for example if I have seq 1,2,3,5... it should update the record 5 with a message "Previous sequence is missing".
Am trying to do this logic using update from inner join statement as follows, although its giving error on line 1 that TblA is ambiguous:
update dbo.TblA
set Msg = 'Previous sequence is missing'
from dbo.TblA R1
left join dbo.TblA R2
on (R2.Sequence = R1.Sequence -1)
and (R2.StatementNumber = R1.StatementNumber)
where R2.TransID is null and R1.Sequence <> 1
I know that this can be easy fixed by nested queries but am thinking of something more organized and neat :)
Use this query. It doesn't set an alias on the table to update, just on the left join.
update dbo.TblA
set Msg = 'Previous sequence is missing'
from dbo.TblA
left join dbo.TblA R
on (R.Sequence = TblA.Sequence -1)
and (R.StatementNumber = TlbA.StatementNumber)
where R.TransID is null and Tbla.Sequence <> 1

Why update query is failing with error Incorrect syntax near the keyword 'GROUP'

This query is not working
update p
set p.TotalAmount = SUM(pay.Amount)
FROM ##tmp t
INNER JOIN SMILAPPLICATION..Purchases p on t.ShoppingCartPaymentID = p.ExternalPurchaseId
INNER JOIN SMILAPPLICATION..PurchaseItems pit ON pit.ExternalPurchaseItemId = t.ShoppingCartID
INNER JOIN SMILAPPLICATION..Sessions s on t.SessionId = s.SessionId
LEFT JOIN SMILAPPLICATION..SessionPricing sp on s.SessionId = sp.SessionId
INNER JOIN SMILAPPLICATION..Payments pay ON pay.PurchaseItemId = pit.PurchaseItemId
GROUP By p.PurchaseId
Msg 156, Level 15, State 1, Line 10 Incorrect syntax near the keyword
'GROUP'.
While this is working
update y
set y.TotalAmount = x.Total
FROM
(
select p.PurchaseId, SUM(pay.Amount) AS Total
FROM ##tmp t
INNER JOIN SMILAPPLICATION..Purchases p on t.ShoppingCartPaymentID = p.ExternalPurchaseId
INNER JOIN SMILAPPLICATION..PurchaseItems pit ON pit.ExternalPurchaseItemId = t.ShoppingCartID
INNER JOIN SMILAPPLICATION..Sessions s on t.SessionId = s.SessionId
LEFT JOIN SMILAPPLICATION..SessionPricing sp on s.SessionId = sp.SessionId
INNER JOIN SMILAPPLICATION..Payments pay ON pay.PurchaseItemId = pit.PurchaseItemId
GROUP By p.PurchaseId
) x inner join SMILAPPLICATION..Purchases y on x.PurchaseId = y.PurchaseId
WHY?
I think this is related to the way UPDATE statement is working behind the scenes and it will not allow any aggregation function:
1) You go to the base table. It cannot have an alias because an alias
would create a working table that would be updated and then disappear
after the statement is finished, thus doing nothing.
2) You go to the WHERE clause. All rows (if any!) that test TRUE are
marked as a subset. If there is no WHERE clause, then the entire table
is marked. The name of this set/pseudo-table is OLD in Standard SQL.
3) You go to the SET clause and construct a set/pseudo-table called
NEW. The rows in this table are build by copying values from the
columns are not mentioned from the original row to the NEW row. The
columns are assigned all at once. That is, the unit of work is a row,
not one column at a time.
4) The OLD subset is deleted and the NEW set is inserted.
I guess that, theoretically, SUM(...) with GROUP BY could be put in a pseudo table and copy the data, but I think UPDATE is meant to work (fast) for direct copy of data.
The GROUP BY is not supported in the UPDATE statement. See MSDN for the complete syntax of the statement.
And it is supported in the <table_source> (FROM)

TSQL Select Statement using Case or Join

I am a little stuck on a situation that I have been trying to fight through. I have a page that allows a user to select all the filter options they want to search by and then it runs the query on that data.
Every field requires something to be picked but on a new field I am introducing, it's going to be optional.
It allows you to provide a list of supervisors and it will then provide all records where the agents supervisor is in the list provided; pretty straight forward. However, I am trying to make this optional as I don't want to always search by users. If I don't provide a name in the UI to pass to the stored procedure, then I want to ignore this part of the statement and get me everything regardless of the manager.
Here is the query I am working with:
SELECT a.[escID],
a.[escReasonID],
b.[ArchibusLocationName],
c.[ArchibusLocationName],
b.[DepartmentDesc],
c.[DepartmentDesc],
a.[escCreatedBy],
a.[escWorkedBy],
a.[escNotes],
a.[preventable],
a.[escalationCreated],
a.[escalationTracked],
a.[feedbackID],
typ.[EscalationType],
typ.[EscalationTypeText] AS escalationType,
d.reasonText AS reasonText
FROM [red].[dbo].[TFS_Escalations] AS a
LEFT OUTER JOIN
red.dbo.EmployeeTable AS b
ON a.escCreatedBy = b.QID
LEFT OUTER JOIN
red.dbo.EmployeeTable AS c
ON a.escWorkedBy = c.QID
LEFT OUTER JOIN
red.dbo.TFS_Escalation_Reasons AS d
ON a.escReasonID = d.ReasonID
INNER JOIN
dbo.TFS_EscalationTypes AS typ
ON d.escType = typ.EscalationType
WHERE B.[ArchibusLocationName] IN (SELECT location
FROM #tmLocations)
AND C.[ArchibusLocationName] IN (SELECT location
FROM #subLocations)
AND B.[DepartmentDesc] IN (SELECT department
FROM #tmDepartments)
AND C.[DepartmentDesc] IN (SELECT department
FROM #subDepartments)
AND DATEDIFF(second, '19700101', CAST (CONVERT (DATETIME, A.[escalationCreated], 121) AS INT)) >= #startDate
AND DATEDIFF(second, '19700101', CAST (CONVERT (DATETIME, A.[escalationCreated], 121) AS INT)) <= #endDate
AND a.[PREVENTABLE] IN (SELECT PREVENTABLE FROM #preventable)
AND b.MgrQID IN (SELECT leaderQID FROM #sourceLeaders)
The part that I am trying to make option is the very last line of the query:
AND b.MgrQID IN (SELECT leaderQID FROM #sourceLeaders)
Essentially, if there is no data in the temp table #sourceLeaders then it should ignore that piece of the query.
In all of the other instances of the WHERE clause, something is always required for those fields which is why that all works fine. I just cant figure out the best way to make this piece optional depending on if the temp table has data in it (the temp table is populated by the names entered in the UI that a user COULD search by).
So this line should be TRUE if something matches data in the table variable OR there is nothing in the table variable
AND
(
b.MgrQID IN (SELECT leaderQID FROM #sourceLeaders)
OR
NOT EXISTS (SELECT 1 FROM #sourceLeaders)
)
Similar to Nick.McDermaid's, but uses a case statement instead :
AND
(
1 = CASE WHEN NOT EXISTS(SELECT 1 FROM #sourceLeaders) THEN 1
WHEN b.MgrQID IN (SELECT leaderQID FROM #sourceLeaders) THEN 1
ELSE 0
END
)
Maybe at the top so you have a single check
DECLARE #EmptySourceLeaders CHAR(1)
IF EXISTS (SELECT 1 FROM #sourceLeaders)
SET #EmptySourceLeaders = 'N'
ELSE
SET #EmptySourceLeaders = 'Y'
Then in the joins
LEFT OUTER JOIN #SourceLeaders SL
ON b.MgrQID = SL.leaderQID
Then in the WHERE
AND (#EmptySourceLeaders = 'Y' OR SL.leaderQID IS NOT NULL)
lots of ways to do it.

Optimize TSQL query with 3 tables

I need to get all the runs from the database, but need to mark if there is an error for this run.
3 Tables:
Runs: contains the runs)
Runfiles: contains the file ids that were processed during a run
Messages: contains errors, warnings, ...
Can this query be optimized any further?
SELECT TOP 1000 runid,
start,
end,
userid,
CASE
WHEN EXISTS(SELECT rf.fk_fileid
FROM runfiles rf
WHERE rf.fk_runid = r.runid
AND EXISTS(SELECT m.messageid
FROM messages m
WHERE m.fk_fileid =
rf.fk_fileid
AND m.fk_statusid = 4))
THEN 1
ELSE 0
END AS ContainsError
FROM runs r
ORDER BY start DESC
Please don't comment on the table names, they were translated for this question.
Thanks!
Try this:
SELECT TOP 1000
r.runid
,r.start
,r.[end]
,r.userid
,CASE WHEN m.messageid IS NOT NULL THEN 1 ELSE 0 END AS ContainsError
FROM runs r
LEFT JOIN runfiles rf
ON rf.fk_runid = r.runid
LEFT JOIN [messages] m
ON m.fk_fileid = rf.fk_fileid
AND m.fk_statusid = 4
ORDER BY r.start DESC
Anything in the select list is ran for each row in the result set. This means that the nested subquery in your CASE statement is being executed for each of those TOP 1000 rows.
Using left joins and a CASE statement to check if the primary key is null allow the entire statement to be evaluated as a set, which SQL Server is built to do. It should perform better this way.

Resources