How can I turn my SELECT INNER JOIN into an UPDATE - sql

I've been trying to turn my SELECT INNER JOIN into an UPDATE.
The query I've made in BigQuery is as follows:
SELECT tt.*
FROM `table` tt
INNER JOIN
(SELECT c_id, MIN(c_orderid) as OrderID, MIN(c_orderdate) AS MinDateTime
FROM `table`
GROUP BY c_id) groupedtt
ON tt.c_orderid = groupedtt.OrderID
AND tt.c_orderdate = groupedtt.MinDateTime
I want to turn this query into an update where it updates c_first_sale and sets it to TRUE.
The query I came up with is:
UPDATE `table` tt
SET tt.c_first_sale = TRUE
FROM `table` as dd
INNER JOIN
(SELECT c_id, MIN(c_orderid) as OrderID, MIN(c_orderdate) AS MinDateTime
FROM `table`
GROUP BY c_id) groupedtt
ON dd.c_orderid = groupedtt.OrderID
AND dd.c_orderdate = groupedtt.MinDateTime
WHERE 1 = 1
But that gives me the following error
UPDATE/MERGE must match at most one source row for each target row
I feel like I'm pretty close, but I'm getting stuck on this.

As mentioned by #Pale, this error occurs when you try to update a table by updating joins with more than one row from the FROM clause as given in this documentation. You can create a separate temporary table, perform a group by using an ID column and export the values into the temporary table. Perform join operation on both the tables using the ID column.

Related

Oracle Sql Duplicate rows when joining new table

I am using oracle sql to join tables. I use the following code:
SELECT
T.TRANSACTION_KEY,
PR.ACCOUNT_KEY,
T.ACCT_CURR_AMOUNT,
T.EXECUTION_LOCAL_DATE_TIME,
TC.DESCRIPTION,
T.OPP_ACCOUNT_NAME,
T.OPP_COUNTRY,
PT.PARTY_TYPE_DESC,
P.PARTY_NAME,
P.CUSTOM_SMALL_STRING_02,
CO.COUNTRY_NAME,
LE.LIST_CD
FROM TRANSACTIONS T
LEFT JOIN TRANSACTION_CODE TC
ON T.TRANSACTION_CODE = TC.ENTITY
LEFT JOIN PARTY_ACCOUNT_RELATION PR
ON T.ACCOUNT = PR.ACCOUNT
LEFT JOIN PARTY P
ON PR.PARTY_KEY = P.PARTY_KEY
LEFT JOIN PARTY_TYPE PT
ON P.PARTY_TYPE = PT.ENTITY
LEFT JOIN COUNTRY CO
ON T.OPP_COUNTRY = CO.ENTITY
LEFT JOIN LISTED_ENTITY LE
ON CO.COUNTRY = LE.ENTITY_KEY
WHERE
PR.PARTY_KEY = '111111111' and T.EXECUTION_LOCAL_DATE_TIME>'2017-01-01';
It works fine until now but I want to join another table which has a column in common(ENTITY_KEY) with PARTY_ACCOUNT_RELATION table (ACCOUNT_KEY) and I want to include some of the new table's columns but when I do that, it becomes dublicated. I am adding the following lines before "where" statment:
LEFT JOIN EVALUATE_RULE ER
ON PR.ACCOUNT_KEY = ER.ENTITY_KEY
Does anyone know where the problem is?
If joining another table into an existing query causes the existing rows to be duplicated, it is because the table being joined in has duplicate values in the columns that are being used as keys for the join
In your case, if you do
SELECT ENTITY_KEY FROM EVALUATE_RULE GROUP BY ENTITY_KEY HAVING COUNT(*) > 1
You'll see which entity_keys are duplicated. When these duplicates are joined to the existing data, the existing data has to be doubled up to permit both rows from EVALUATE_RULE with the same ENTITY_KEY to exist in the result set
You must either de-dupe the table, or put other clauses into your ON condition to further restrict the rows coming from EVALUATE_RULE.
For example, after adding EVALUATE_RULE and putting ER.* in your SELECT list, imagine that you can see that the rows from ER are status = 'old' and status = 'current' but you know you only want the current ones.. So put AND er.status = 'current' in your ON clause
Your comment indicates that multiple records differ by some column you don't care about, so this technique will just select only one row:
LEFT JOIN
(SELECT e.*, ROW_NUMBER() OVER(PARTITION BY e.entity_key ORDER BY e.name) as rown FROM evaluate_rule e) er
ON
er.entity_key = pr.account_key and
er.rown = 1
If you want info on why this works, run that sql in isolation:
SELECT e.*, ROW_NUMBER() OVER(PARTITION BY e.entity_key ORDER BY e.name) as rown FROM evaluate_rule e
ORDER BY e.entity_key -- i added this to make it more clear what is going on. You don't need it in your main query
It just assigns a number to each row in the table, the number restarts at 1 every time entity_key changes, so we can then select all those with rown = 1
If it turns out you DO want something specific like "the latest row from evaluate_rule", you can use something like this:
SELECT e.*, ROW_NUMBER() OVER(PARTITION BY e.entity_key ORDER BY e.created_date DESC) as rown FROM evaluate_rule e
Now the latest created_date row will always have rown = 1
So far as I can understain from your description, table EVALUATE_RULE has moro records with ACCOUNT_KEY=ENTITY_KEY.
You can change your query section:
LEFT JOIN EVALUATE_RULE ER ON PR.ACCOUNT_KEY = ER.ENTITY_KEY
to
LEFT JOIN (SELECT DISTINCT ENTITY_KEY FROM EVALUATE_RULE) ER ON PR.ACCOUNT_KEY = ER.ENTITY_KEY
If you post structure of EVALUATE_RULE (indicating PK columns) I can change my answer to let you includ EVALUATE_RULE columns in final query.

How to display last updated record for multiple records

I have joined two tables to pull the data I need. I'm having trouble only displaying the most current record from one table. What I'm trying to do is look for the last updated value. I have tried to incorporate max() and row_num but have not had any success.
Here is what I currently have:
select distinct t1.CaId,t1.Enrolled,t1.Plan,t2.Category,t2.updateddate
from table.one(nolock) t1
inner join table.two(nolock) t2 on t1.CaId=t2.CaID
where t1.coverageyear=2016
and right(t1.Plan,2)<>left(t2.Category,2)
order by 5 desc
You can join your main query with a subquery that just grabs the last update date for each ID, like this:
select all_rec.CaId, all_rec.Enrolled, all_rec.[Plan], all_rec.Category, all_rec.updateddate
from
(select distinct t1.CaId,t1.Enrolled,t1.[Plan],t2.Category,t2.updateddate
from [table.one](nolock) t1
inner join [table.two](nolock) t2 on t1.CaId=t2.CaID
where t1.coverageyear=2016
and right(t1.[Plan],2)<>left(t2.Category,2)
) as all_rec
inner join
(SELECT max(updateddate) AS LAST_DATE, CaId
FROM [table.two](nolock)
GROUP BY CaId)
AS GRAB_DATE
on (all_rec.Ca_Id = GRAB_DATE.Ca_Id)
and (all_rec.updateddate = GRAB_DATE.updateddate)
order by 5 desc
I added brackets around your usages of table and Plan because those are SQL reserved words.
If you are trying to get last updated value, then just simply add to your query:
order by t2.updateddate desc
It will show most current record from tables.

Sql Update Count Details after grouping

I have multiple tables in my SQL Server database.
I have one table say Table A which has fields like dispatch,filename,etc.
The second table say Table B has filedetails like filename, dispatchcount, totalcount etc.
There are many other fields in both tables but not relevant to this question.
Requirement is :
I want to update Table B dispatch count after grouping Table A customers where dispatch is Y.
As I want to update the Table B using the result of grouping should I create a temp Table of the result or please guide:
Query:
update Collation_Data set Dqty=T.count1
from (select [collation_code],count(1) as 'count1'
FROM [Tatkal].[dbo].[Tatkal_Merge] T
where Dscan='Y'
group by [collation_code]) where srno=T.[collation_code]
In SQL Server, you can use a join with an aggregation query. I want to point out that you should use left join if you want to update all rows in collation_data, even those with no matches:
update c
set c.Dqty = cm.cnt
from Collation_Data c left join
(select collation_code, count(*) as cnt
from [Tatkal].[dbo].[Tatkal_Merge] m
where Dscan = 'Y'
group by collation_code
) cm
on c.srno = cm.collation_code;
You can also do this with a correlated subquery:
update Collation_Data c
set Dqty = (select count(*)
from [Tatkal].[dbo].[Tatkal_Merge] m
where m.Dscan = 'Y' and m.collation_code = c.collation_code
);
This can be quite efficient with an index on Tatkal_Merge(collation_code, Dscan).
I want to update the Table B using the result of grouping should I create a temp Table of the result or please guide
update c
set c.Dqty=T.count1
from Collation_Data c
join
(select [collation_code],count(1) as 'count1'
FROM [Tatkal].[dbo].[Tatkal_Merge]
where Dscan='Y'
group by [collation_code])t
on c.srno=T.[collation_code]

Update using Distinct SUM

I have found a few good resources that show I should be able to merge a select query with an update, but I just can't get my head around of the correct formatting.
I have a select statement that is getting info for me, and I want to pretty much use those results to Update an account table that matches the accountID in the select query.
Here is the select statement:
SELECT DISTINCT SUM(b.workers)*tt.mealTax as MealCost,b.townID,b.accountID
FROM buildings AS b
INNER JOIN town_tax AS tt ON tt.townID = b.townID
GROUP BY b.townID,b.accountID
So in short I want the above query to be merged with:
UPDATE accounts AS a
SET a.wealth = a.wealth - MealCost
Where MealCost is the result from the select query. I am sure there is a way to put this into one, I just haven't quite been able to connect the dots to get it to run consistently without separating into two queries.
First, you don't need the distinct when you have a group by.
Second, how do you intend to link the two results? The SELECT query is returning multiple rows per account (one for each town). Presumably, the accounts table has only one row. Let's say that you wanted the average MealCost for the update.
The select query to get this is:
SELECT accountID, avg(MealCost) as avg_Mealcost
FROM (SELECT SUM(b.workers)*tt.mealTax as MealCost, b.townID, b.accountID
FROM buildings AS b INNER JOIN
town_tax AS tt
ON tt.townID = b.townID
GROUP BY b.townID,b.accountID
) a
GROUP BY accountID
Now, to put this into an update, you can use syntax like the following:
UPDATE accounts
set accounts.wealth = accounts.wealth + asum.avg_mealcost
from (SELECT accountID, avg(MealCost) as avg_Mealcost
FROM (SELECT SUM(b.workers)*tt.mealTax as MealCost, b.townID, b.accountID
FROM buildings AS b INNER JOIN
town_tax AS tt
ON tt.townID = b.townID
GROUP BY b.townID,b.accountID
) a
GROUP BY accountID
) asum
where accounts.accountid = asum.accountid
This uses SQL Server syntax, which I believe is the same as for Oracle and most other databases. Mysql puts the "from" clause before the "set" and allows an alias on "update accounts".

update a sql using subquery and aggregate function

Below is a SQL query where I am getting the error
returns more than 1 value
Query:
update Tab2
set monthly_calls =
(select a.month
from
(select accountid, max(annual_calls)/12 as month
from cpo
group by accountid) a
inner join tab2 on tab2.accountid = a.accountid)
FYI... my select query which is left of = is working fine
If you select query which is left of = returns multiple records then it will not be executed.
So please try to get only 1 record out of that.
Only one record can be updates at a time using this update statement, so you must make sure that only one record is returned by that query.
Try this.
update Tab2
set monthly_calls =
(select top 1 a.month
from
(select accountid, max(annual_calls)/12 as month
from cpo
group by accountid) a
inner join tab2 on tab2.accountid = a.accountid)
update Tab2
set monthly_calls = a.month
from
(select accountid, max(annual_calls)/12 as month
from cpo
group by accountid) a
inner join tab2 on tab2.accountid = a.accountid
I think the logic you're using needs to be reviewed as well. The column annual_calls - does it get updated once a year, or is it updated at the end of every month? The reason I ask is if this were for a new system with only 6 months worth of calls, the monthly_calls field would be set to a value of approximately half of what it should be.
I would suggest also renaming monthly_calls to avg_monthly_calls or something more meaningful because if someone were to come in, look at your database schema and try to work out what is what, they might think that field holds total monthly calls or expected monthly calls.
As for the statement to update the data - without seeing any sort of data, here's a neater way of using an UPDATE statement without subselects:
UPDATE
Tab2
SET
monthly_calls = MAX(annual_calls) / 12
FROM
CPO INNER JOIN Tab2 ON CPO.accountid = Tab2.accountid
WHERE
YEAR([Tab2.DateField]) = #YearToUpdate
GROUP BY
CPO.accountid
This basically takes care of the subselect by including a FROM clause in the UPDATE statement. That way you can join and update all at once.
Edit: I would also add in a WHERE clause so you can set it so you update only the current year, or the previous year, or whatever year/period you pass in. That way, you ensure you only get one record for each account for each year.
You are trying to set monthly_calls variable's value by using a select statement that returns a set of rows. Try using an aggregate function on a.month like max().
Standard SQL requires scalar subqueries, which in this case is arguably simpler than joins anyhow:
UPDATE Tab2
SET monthly_calls = (
SELECT MAX(annual_calls) / 12
FROM cpo
WHERE tab2.accountid = cpo.accountid
)
WHERE EXISTS (
SELECT *
FROM cpo
WHERE tab2.accountid = cpo.accountid
);