Set two values in one table based on sum of a column in another table - mysql

I have two tables, a "data" table with hundreds of thousands of rows, and a "settings" table with hundreds of rows. I need to update two values in the settings table, based on the sum of one column in the data table being greater than another column in the settings table, where the 'id' of the settings table matches the 'offerid' of the data table.
I've been cruising Google looking at many possible solutions, but I don't seem able to bend any of them to my needs. Any time I start working with Joins, I inevitably get tripped up.
My latest attempt which did not work, is:
UPDATE settings a, data b
SET a.hidden=1, a.weight=0
WHERE a.id = b.offerid
AND sum(b.views) > a.target
I liked this flavor of approach as I thought it actually made sense to me, but all I get is "Error code 1111: Invalid us of group function". Maybe someone here can point me in the right direction.

It errors out because you are using an aggregate function (sum) with no grouping. So query really does not know which rows should be summed together. You can use a subquery like this:
UPDATE settings a
INNER JOIN
(
SELECT
sum(b.views) sumviews, b.offerid
from data b
group by b.offerid
) c on c.offerid = a.id
SET a.hidden=1, a.weight=0
where c.sumviews>a.target
EDIT: To disable the safe update function do the following steps
Follow the steps below before executing the UPDATE command:
Go to Edit --> Preferences
Click "SQL Queries" tab and uncheck "Safe Updates" check box
Query --> Reconnect to Server
Now execute your sql query
Source: Disable Safe Update

Try using
HAVING
instead of
AND
So your code goes like:
UPDATE settings a, data b
SET a.hidden=1, a.weight=0
WHERE a.id = b.offerid
GROUP BY a.id
HAVING sum(b.views) > a.target

Related

MySQL: trouble copying data from one table to another based on matching ids

i need to copy existing data from one table to another based on matching fields. this is not a wholesale column dupe.
how it should work is: if the id in asset matches the assetID in report, then copy the repEmail field from the report table into the rep_email field in the asset table. this is what i've built based on the MySQL docs for insert...select:
INSERT INTO asset (rep_email)
SELECT report.repEmail
FROM report WHERE report.assetID = asset.id
this throws: Error in query (1054): Unknown column 'asset.id' in 'where clause'.
i also tried this:
where safety_report.eNumber = asset_roster.id
insert into asset_roster (building_rep_email)
select bldgRepEmail from safety_report;
but it errors on the first line. :(
i'm not sure how to modify this to eliminate the error and properly write the values across. it seems like it should be a simple operation... please advise.
NB: i do not have admin access to the tables. the reason for this seeming duplication has to do with permissions to the different tables. and even if not, i'd need to write foreign keys anyway...
I think that you want to update the column rep_email of asset with values from the column repEmail of report:
UPDATE asset a
INNER JOIN report r ON r.assetID = a.id
SET a.rep_email = r.repEmail

What are the consequences of turning off only_full_group_by sql_mode?

I am collecting SSH brute force data, and store it in a table called "attempts". I want to GROUP BY each IP address with the associated location data and country name, but I cannot do this with only_full_group_by enabled. I disabled it and my query works fine, but I have two questions:
What are the consequences of disabling only_full_group_by? I assume it is a default for a reason, why is that? I can see issues if the same IP address had different location data for each record, but is that the only scenario where things go wrong?
If I wanted to accomplish the query without disabling only_full_group_by, what would that query look like?
My code:
SELECT latitude,longitude,country_name,foreign_ip,g.count as counter
FROM attempts
LEFT OUTER JOIN (
SELECT COUNT(foreign_ip) as count,foreign_ip as fi
FROM attempts
GROUP BY foreign_ip
) as g on attempts.foreign_ip = g.fi
GROUP BY foreign_ip;
I assume it is a default for a reason, why is that?
The only_full_group_by prevents using GROUP BY in ways that are incorrect. Having it on by default is a good thing.
You can write the query without the subquery:
SELECT latitude,longitude,country_name, foreign_ip,count(*) as counter
FROM attempts
GROUP BY latitude,longitude,country_name, foreign_ip
MySQL has historically been very tolerant with user errors. That often leads to additional work (e.g. first you store dates and then you need to filter invalid ones one every select) and data loss (your column is too short for the data and you end up with truncated data). They're trying to fix that but they cannot break a million apps that rely on tolerant behaviour so the solution has been to add optional SQL models.
If all columns within groups have the same values, you're right, nothing will break. The problem is when that isn't true. MySQL will not warn you and, instead, will just retrieve an arbitrary (not even random) row per group.
Your current query can be easily fixed to work in either mode:
SELECT latitude,longitude,country_name,foreign_ip,g.count as counter
FROM attempts
LEFT OUTER JOIN (
SELECT COUNT(foreign_ip) as count,foreign_ip as fi
FROM attempts
GROUP BY foreign_ip
) as g on attempts.foreign_ip = g.fi
GROUP BY latitude,longitude,country_name,foreign_ip,g.count

Read-only after inner join (MySQL)

In MySQL workbench (Mac OS), I wanted to join two tables so that I can update the second one. The code I put in was as follows
select f.company, f.remarks, c.pic
from feedback f, customers c
where f.col = c.col
order by f.company;
The output is a read only table, which prevented me from updating table "customers" based on the f.remarks column.
Your advice/suggestion is appreciated. Thank you.
By hovering above the "Read Only" icon, I got the following message:
"Statement must be a SELECT from a single table with a primary key for its results to be editable".
After some research based on the advice given by fellow coders, here are some points to note:
In MySQL workbench, one cannot edit the results obtained from any JOINs because it's not from a single table;
In using SELECT from a single table, the primary key must be included in order for the result to be editable.
Thank you to everyone who contributed to the question. I appreciate it.
The problem is because, as you mentioned, SELECT only returns a "read only" result set.
So basically you cant use MySQL workbench to update a field in a read only result set that is returned when using a JOIN statement.
from what i understand you want to update table "customers" based on a query.
maybe this post will help you:
update table based on subquery of table

SQL newbie: execution order of subqueries?

Warning: This is a soft question, where you'll be answering to someone who has just started teaching himself SQL from the ground up. I haven't gotten my database software set up yet, so I can't provide tables to run queries against. Some patience required.
Warnings aside, I'm experimenting with basic SQL but I'm having a little bit of a rough time getting a clear answer about the inner workings of subqueries and their execution order within my query.
Let us say my query looks something like shit:
SELECT * FROM someTable
WHERE someFirstValue = someSecondValue
AND EXISTS (
SELECT * FROM someOtherTable
WHERE someTable.someFirstValue = someOtherTable.someThirdValue
)
;
The reason I'm here, is because I don't think I understand fully what is going on in this query.
Now I don't want to seem lazy, so I'm not going to ask you guys to "tell me what's going on here", so instead, I'll provide my own theory first:
The first row in someTable is checked so see if someFirstValue is the same as someSecondValue in that row.
If it isn't, it goes onto the second row and checks it too. It continues like this until a row passes this little inspection.
If a row does pass, it opens up a new query. If the table produced by this query contains even a single row, it returns TRUE, but if it's empty it returns FALSE.
My theory ends here, and my confusion begins.
Will this inner query now compare only the rows that passed the first WHERE? Or will it check all the items someTable and someOtherTable?
Rephrased; will only the rows that passed the first WHERE be compared in the someTable.someFirstValue = someOtherTable.someThirdValue subquery?
Or will the subquery compare all the elements from someTable to all the elements in someOtherTable regardless of which passed the first WHERE and which didn't?
UPDATE: Assume I'm using MySQL 5.5.32. If that matters.
The answer is that SQL is a descriptive language that describes the result set being produced from a query. It does not specify how the query is going to be run.
In your case the query has several options on how it might run, depending on the database engine, what the tables look like, and indexes. The query itself:
SELECT t.*
FROM someTable t
WHERE t.someFirstValue = t.someSecondValue AND
EXISTS (SELECT *
FROM someOtherTable t2
WHERE t.someFirstValue = t2.someThirdValue
);
Says: "Get me all columns from SomeTable where someFirstValue = someSecondValue and there is a corresponding row in someOtherTable where that's table column someThirdValue is the same as someFirstValue".
One possible way to approach this query would be to scan someTable and first check for the first condition. When the two columns match, then look up someFirstValue in an index on someOtherTable(someThirdValue) and keep the row if the values match. As I say, this is one approach, and there are others.

SQL update query for balances using Access raises 'Operation must use an updateable query'

I have the following query (MS Access 2010) which I'm trying to use to update a table with a running balance:
UPDATE Accounts a SET a.CurrentBalance =
(SELECT sum(iif(c.categoryid = 2,t.Amount * -1, t.Amount)) +
(select a1.openingbalance
from accounts a1 where a1.accountid = a.accountid) AS TotalAmount
FROM transactions t inner join (
transactiontypes tt inner join
categories c on c.categoryid = tt.categoryid)
on t.transactiontypeid = tt.transactiontypeid);
The tables used are:
A work around for the "Query must use an updateable query" is to use a temporary table and then update the final target data based on the aggregated data in the temporary table. In your case, as mwolfe suggests, you have an aggregate function in the inner select query. The workaround could provide a quick fix for this situation, as it has for me.
Ref: http://support.microsoft.com/kb/328828
This article helped me understand the specifics of the situation and provided the work around:
http://www.fmsinc.com/MicrosoftAccess/query/non-updateable/index.html
You cannot use aggregate functions (like SUM) in an update query. See Why is my query read-only? for a full list of conditions that will cause your query to be "non-updateable".
The Access db engine includes support for domain functions (DMax, DSum, DLookup, etc.). And domain functions can often allow you to circumvent non-updateable query problems.
Consider DSum() with these 3 rows of data in MyTable.
id MyNumber
1 2
2 3
3 5
Then in the Immediate window, here are 2 sample DSum() expressions.
? DSum("MyNumber", "MyTable")
10
? DSum("IIf(id=1,MyNumber * -1, MyNumber)", "MyTable")
6
I think you may be able to use something like that second expression as a replacement for the sum(iif(c.categoryid = 2,t.Amount * -1, t.Amount) part of your query.
And perhaps you can use a DLookup() expression to get your TotalAmount value. Unfortunately I got frustrated trying to translate your current SQL to domain functions. And I realize this isn't a complete solution, but hope it will point you to something useful. If you edit your question to show us brief samples of the starting data and what you hope to achieve from your UPDATE statement based on that sample data, I would be willing to have another look at this.
Finally, consider whether you absolutely must store CurrentBalance in a table. As a rule of thumb, avoid storing derived values. Instead, use a SELECT query to compute the derived value when you need it. That approach would guarantee CurrentBalance is always up-to-date whenever you retrieve it. It would also spare you the effort to create a working UPDATE statement.