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

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

Related

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

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.

SQL Case Statement with SELECT inside INNER JOIN. Error of multi-part could not be bound

I am writing an INNER JOIN that added fields like CompanyId etc..
But in my CASE inside the INNER JOIN I am getting an error after the WHERE
The error say on the IR.MyId and ICB1.MyId
"multi-part identifier could not be bound"
My intention for this query is to add a CASE inside the INNER JOIN.
So the first column is A.CompanyId. The second column will be the CASE statement to check if the ID exists in another table. If not, it will do another SELECT statement, ELSE just MyID.
But I get an error on the SELECT COUNT(*) inside the CASE WHEN.
Any help is appreciated. Thanks.
INNER JOIN
(SELECT DISTINCT
A.CompanyId,
CASE
WHEN (SELECT count(*)
FROM Table1 IBC1
WHERE IR.MyId = ICB1.MyId) == 0
THEN 1
ELSE 0
END
FROM
cmp.CompanyNews A) E ON NP.CompanyId = E.CompanyId
I don't have your full query, but if it's viable I'd take the following approach to this:
...
CASE
WHEN ICB1_ID.ID IS NULL
THEN 1
ELSE 0
END [ColumnName]
FROM
...
INNER JOIN
(
SELECT DISTINCT A.CompanyId
FROM cmp.CompanyNews A
) E
ON NP.CompanyId = E.CompanyId
OUTER APPLY
(
SELECT TOP 1 ICB1.MyID ID
FROM Table1 IBC1
WHERE ICB1.MyId = IR.MyId
) ICB1_ID

multirow SQL Update not working as expected - SELECT result different from UPDATE result

I have a multiple row update query that is not working like I expect it to. The JOIN condition is adhered to in the SELECT statement, but not in the UPDATE statement.
In other words: I changed 1 row, the SELECT query shows only 1 result (as expected). I convert the query to an UPDATE statement and run it - all rows in the table are changed to the same value - the JOIN condition is totally ignored.
I thought it was due to a table variable, so I used a temp table and get the same results. I verified my JOIN condition is comparing the same datatype (it's an INNER JOIN). I'm at a loss as to why this will not work
SELECT
(o.SubTotal - o.OrderDiscounts) AS OrderSubTotal,
t.CommRate * (o.SubTotal - o.OrderDiscounts) AS CommDue,
'MODIFIED' AS "Status"
FROM
bvc_Order o
INNER JOIN
#tbl t ON o.OrderNumber = t.OrderNumber
Converted to an UPDATE statement:
UPDATE AffiliateComm
SET [OrderSubtotal] = (o.SubTotal - o.OrderDiscounts),
[CommDue] = t.CommRate * (o.SubTotal - o.OrderDiscounts),
[Status] = 'MODIFIED'
FROM
bvc_Order o
INNER JOIN
#tbl t ON o.OrderNumber = t.OrderNumber
I've done the same query with a table variable - (select works, update always updates all rows & ignores JOIN condition) no joy.
Stranger still, if I put a WHERE clause on the end, it is IGNORED!
As in
WHERE o.OrderNumber = t.OrderNumber
I've done queries like these before and never had this problem.
The output of the SELECT query (as an example when 1 row needs changing)
OrderSubTotal CommDue Status
----------------------------------
1285.20 38.56 MODIFIED
WHEN I run the update query:
(1 row(s) affected) <-This is the temp table (or variable) having 1 row inserted as it should CORRECT
(5 row(s) affected) <- This is the INCORRECT number of rows affected by the UPDATE (should be 1)
Is there a setting in SQL Server 2012 that is wrong? If you inner join a table with 1 row, it's not possible to have MORE than 1 row as a result set, right?
I'm baffled.
Presumably, you intend something like this:
UPDATE ac
SET [OrderSubtotal] = (o.SubTotal - o.OrderDiscounts),
[CommDue] = t.CommRate*(o.SubTotal - o.OrderDiscounts),
[Status] = 'MODIFIED'
FROM AffiliateComm ac INNER JOIN
bvc_Order o
ON ac.OrderNumber = o.OrderNumber INNER JOIN
#tbl t
ON o.OrderNumber = t.OrderNumber ;
In other words, AffiliateComm needs to be joined into the other tables, somehow. Otherwise, all rows in AffiliateComm will be updated, I believe with the same value. I made up an AffiliateId for the above query.
In your update:
UPDATE AffiliateComm
SET [OrderSubtotal]=(o.SubTotal - o.OrderDiscounts), [CommDue]=t.CommRate*(o.SubTotal - o.OrderDiscounts),[Status]='MODIFIED'
FROM bvc_Order o
INNER JOIN #tbl t
ON o.OrderNumber = t.OrderNumber
You tell sql server to update a table called AffiliateComm. However, this table is not included in your join.
Not knowing the schema of this table, i can only guess at whata right, possibly something like:
UPDATE a
SET [OrderSubtotal]=(o.SubTotal - o.OrderDiscounts), [CommDue]=t.CommRate*(o.SubTotal - o.OrderDiscounts),[Status]='MODIFIED'
FROM AffiliateComm A
INNER JOIN bvc_Order o
ON o.OrderNumber = a.OrderNumber
INNER JOIN #tbl t
ON o.OrderNumber = t.OrderNumbe

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)

Update a table using JOIN in SQL Server?

I want to update a column in a table making a join on other table e.g.:
UPDATE table1 a
INNER JOIN table2 b ON a.commonfield = b.[common field]
SET a.CalculatedColumn= b.[Calculated Column]
WHERE
b.[common field]= a.commonfield
AND a.BatchNO = '110'
But it is complaining :
Msg 170, Level 15, State 1, Line 2
Line 2: Incorrect syntax near 'a'.
What is wrong here?
You don't quite have SQL Server's proprietary UPDATE FROM syntax down. Also not sure why you needed to join on the CommonField and also filter on it afterward. Try this:
UPDATE t1
SET t1.CalculatedColumn = t2.[Calculated Column]
FROM dbo.Table1 AS t1
INNER JOIN dbo.Table2 AS t2
ON t1.CommonField = t2.[Common Field]
WHERE t1.BatchNo = '110';
If you're doing something silly - like constantly trying to set the value of one column to the aggregate of another column (which violates the principle of avoiding storing redundant data), you can use a CTE (common table expression) - see here and here for more details:
;WITH t2 AS
(
SELECT [key], CalculatedColumn = SUM(some_column)
FROM dbo.table2
GROUP BY [key]
)
UPDATE t1
SET t1.CalculatedColumn = t2.CalculatedColumn
FROM dbo.table1 AS t1
INNER JOIN t2
ON t1.[key] = t2.[key];
The reason this is silly, is that you're going to have to re-run this entire update every single time any row in table2 changes. A SUM is something you can always calculate at runtime and, in doing so, never have to worry that the result is stale.
Try it like this:
UPDATE a
SET a.CalculatedColumn= b.[Calculated Column]
FROM table1 a INNER JOIN table2 b ON a.commonfield = b.[common field]
WHERE a.BatchNO = '110'
Answer given above by Aaron is perfect:
UPDATE a
SET a.CalculatedColumn = b.[Calculated Column]
FROM Table1 AS a
INNER JOIN Table2 AS b
ON a.CommonField = b.[Common Field]
WHERE a.BatchNo = '110';
Just want to add why this problem occurs in SQL Server when we try to use alias of a table while updating that table, below mention syntax will always give error:
update tableName t
set t.name = 'books new'
where t.id = 1
case can be any if you are updating a single table or updating while using join.
Although above query will work fine in PL/SQL but not in SQL Server.
Correct way to update a table while using table alias in SQL Server is:
update t
set t.name = 'books new'
from tableName t
where t.id = 1
Hope it will help everybody why error came here.
MERGE table1 T
USING table2 S
ON T.CommonField = S."Common Field"
AND T.BatchNo = '110'
WHEN MATCHED THEN
UPDATE
SET CalculatedColumn = S."Calculated Column";
UPDATE mytable
SET myfield = CASE other_field
WHEN 1 THEN 'value'
WHEN 2 THEN 'value'
WHEN 3 THEN 'value'
END
From mytable
Join otherTable on otherTable.id = mytable.id
Where othertable.somecolumn = '1234'
More alternatives here.
Seems like SQL Server 2012 can handle the old update syntax of Teradata too:
UPDATE a
SET a.CalculatedColumn= b.[Calculated Column]
FROM table1 a, table2 b
WHERE
b.[common field]= a.commonfield
AND a.BatchNO = '110'
If I remember correctly, 2008R2 was giving error when I tried similar query.
I find it useful to turn an UPDATE into a SELECT to get the rows I want to update as a test before updating. If I can select the exact rows I want, I can update just those rows I want to update.
DECLARE #expense_report_id AS INT
SET #expense_report_id = 1027
--UPDATE expense_report_detail_distribution
--SET service_bill_id = 9
SELECT *
FROM expense_report_detail_distribution erdd
INNER JOIN expense_report_detail erd
INNER JOIN expense_report er
ON er.expense_report_id = erd.expense_report_id
ON erdd.expense_report_detail_id = erd.expense_report_detail_id
WHERE er.expense_report_id = #expense_report_id
Another approach would be to use MERGE
;WITH cteTable1(CalculatedColumn, CommonField)
AS
(
select CalculatedColumn, CommonField from Table1 Where BatchNo = '110'
)
MERGE cteTable1 AS target
USING (select "Calculated Column", "Common Field" FROM dbo.Table2) AS source ("Calculated Column", "Common Field")
ON (target.CommonField = source."Common Field")
WHEN MATCHED THEN
UPDATE SET target.CalculatedColumn = source."Calculated Column";
-Merge is part of the SQL Standard
-Also I'm pretty sure inner join updates are non deterministic..
Similar question here where the answer talks about that
http://ask.sqlservercentral.com/questions/19089/updating-two-tables-using-single-query.html
I think, this is what you are looking for.
UPDATE
Table1
SET
Table1.columeName =T1.columeName * T2.columeName
FROM
Table1 T1
INNER JOIN Table2 T2
ON T1.columeName = T2.columeName;
I had the same issue.. and you don't need to add a physical column.. cuz now you will have to maintain it..
what you can do is add a generic column in the select query:
EX:
select tb1.col1, tb1.col2, tb1.col3 ,
(
select 'Match' from table2 as tbl2
where tbl1.col1 = tbl2.col1 and tab1.col2 = tbl2.col2
)
from myTable as tbl1
Aaron's approach above worked perfectly for me. My update statement was slightly different because I needed to join based on two fields concatenated in one table to match a field in another table.
--update clients table cell field from custom table containing mobile numbers
update clients
set cell = m.Phone
from clients as c
inner join [dbo].[COSStaffMobileNumbers] as m
on c.Last_Name + c.First_Name = m.Name
Those who are using MYSQL
UPDATE table1 INNER JOIN table2 ON table2.id = table1.id SET table1.status = 0 WHERE table1.column = 20
Try:
UPDATE table1
SET CalculatedColumn = ( SELECT [Calculated Column]
FROM table2
WHERE table1.commonfield = [common field])
WHERE BatchNO = '110'

Resources