update a sql using subquery and aggregate function - sql

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
);

Related

How can I turn my SELECT INNER JOIN into an UPDATE

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.

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.

Access Inner Join to select a record with the Max Date to distinctively select a record from another table

I am using Access and writing a query to join another table which has multiple records with the same ID. So, I only need to fetch the record with the MAX date to fetch only 1 record. I keep getting an syntax error.
Here is my normal Inner Join to the other table:
SELECT
table2.NameDesc,
FROM [table1]
INNER JOIN
(
SELECT [MyDataTable].[ID], [MyDataTable].[NameDesc], MAX( [MyDataTable].[MyDate]) as RecentDate
FROM [MyDataTable]
GROUP BY [MyDataTable].[ID]
) table2
ON [table1].[ID] = table2.[ID];
Normally on my inner join statement, I write like this:
INNER JOIN [table3] ON [table1].[ID] = [table3].[ID])
So I need to fetch only 1 record with the MAX date. I finally was able to
click save but when I click on the DateSheetView, I get the following error:
"Your query does not include the specified expression "NameDesc" as part of an aggregate function"
I am new the access query with the [ ] bracket which is different from sql server.
How can I get this Access query to work and fetch the record with the max date from another table?
Any help is appreaciated.
Thanks
When you use an aggregate function, like MAX, you need to include all other columns you are returning as part of the group by. In order for the query to run without errors, you can do this:
SELECT
table2.NameDesc
FROM [table1]
INNER JOIN
(
SELECT [MyDataTable].[ID],
[MyDataTable].[NameDesc],
MAX([MyDataTable].[MyDate]) as RecentDate
FROM [MyDataTable]
GROUP BY [MyDataTable].[ID],
[MyDataTable].[NameDesc]
) table2
ON [table1].[ID] = table2.[ID];

Selecting nth row in Inner Join on Microsoft SQL Server 2012

My gifts table has multiple rows that match the criteria in the inner join statement below. I want to select a specific row (3rd, 5th or 32nd row as examples). I have tried using Row_Number(), but it is a windowed function and needs to be within the select part (as far as I know), but that then throws errors, because the overall statement has to return only one value.
I have also tried using top and then selecting the bottom value of top (top 5 for example and then selecting the last value), but I was unable to get that to work. With the statement below, how can I modify it to also select the nth row?
update output
set output.gift_date01 = (select gifts.gift_date
where
gifts.gift_date >= '2015-1-1 00:00:00'
and gifts.gift_date <= '2015-12-31')
from output
inner join gifts on output.donor_id = gifts.donor_id;
Thank you!
I think that this will get you close if it's not exactly what you're looking to do.
;WITH CTE_Gifts AS
(
SELECT
G.donor_id,
G.gift_date,
ROW_NUMBER() OVER (PARTITION BY donor_id ORDER BY gift_date) AS row_num
FROM
dbo.Gifts G
WHERE
G.gift_date BETWEEN #start_date AND #end_date
)
UPDATE O
FROM
[Output] O -- Should be renamed to not a reserved keyword
INNER JOIN CTE_Gifts C ON
C.donor_id = O.donor_id AND
C.row_num = #some_row_number

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".