Update with nested subquery (sum) to get restricton on update clause - mysql

Got this bit of SQL as an update script, I've tried to add a work round to not being able to include the table to be updated as a clause in the statement so using sub queries, but struggling to get this to work.
Essientially I need update a vailue in table 1 with the summation of a field in table 2, but only where the two other fields match across a couple of tables and based on field6 the restriction is applied to the update clause.
UPDATE table1 W SET Field1=(SELECT field2 FROM
(SELECT A.id, B.field3, SUM(A.field2) AS field2
FROM table2 A, table3 B, table4 P
WHERE A.id=B.id AND P.field6=B.field6) B ) WHERE W.field6=B.field6
In the real world example, select the sum of points conceded in a rugby game when a rugby player has participated in the match. table 2 has the results (including the score) table 3 has the team sheets and table 1 and 4 are the same player table to be updated.
Hopefully this is clear enough and someone can point me in the right direction.
Tried the following:
UPDATE $WSLKEEP W, $WSLFIX A, $WSLFIXPLAY B
SET W.F_CONCEDED=SUM(A.F_AGAINST)
WHERE A.F_ID=B.F_GAMEID
AND B.F_NAME=W.F_NAME"
but now stuck with:
Invalid use of group function
Kind regards

It seems like your subquery should be grouping on field6 and exposing that column for inner join with table1. Here's how you do that in MySQL:
UPDATE table1 W
INNER JOIN (
SELECT B.field6, SUM(A.field2) AS field2
FROM table2 A, table3 B, table4 P
WHERE A.id=B.id AND P.field6=B.field6
GROUP BY B.field6
) B ON W.field6=B.field6
SET W.Field1 = B.Field2
And while we are at it, I would also recommend you to refrain from (ab)using comma joins in favour of explicit joins. The latter, however unusual at first after being long accustomed to a different syntax, can very soon become habitual and much more intuitive than the former. A great deal has been said on the topic, and some people may be holding quite strong opinions about comma joins. I say, comma joins can still have their share of use. However, when you are joining on a condition, the current ANSI syntax should be your choice.
Here's the above statement with the subquery transformed so as to use explicit joins:
UPDATE table1 W
INNER JOIN (
SELECT B.field6, SUM(A.field2) AS field2
FROM table2 A
INNER JOIN table3 B ON A.id = B.id
INNER JOIN table4 P ON P.field6 = B.field6
GROUP BY B.field6
) B ON W.field6 = B.field6
SET W.Field1 = B.Field2

For an update query like you have above, you are allowed to include multiple tables in the UPDATE clause, even if you aren't updating all of them. This will make sub-queries unnecessary and speed the execution quite a bit. For example, you can do something like this.
UPDATE table1 W, table2 A, table3 B, table4 P
SET W.Field1 = SUM(A.field2) ...
I'm unclear on the specifics of what you are trying to update exactly, but I just wanted to put out that you can often avoid sub-queries by using this kind of syntax.

Related

Why does this "nested join" works with PDO but not with MySql cli?

I don't understand why this query works and what it actually means. It should be noted that this query works when using PDO but doesn't through Mysql cli or even phpmyadmin.
SELECT table1.something, table2.something
FROM someTable
LEFT JOIN table 1
INNER JOIN table2
ON table2.table1_id = table1.id
ON table1.account_id = someTable.account_id
No errors are thrown and, even weirder, I actually have results that are coherent with what the query is supposed to do.
This is valid SQL syntax, assuming you have just taken a snippet from the FROM clause. It is interpreted as:
SELECT table1.something, table2.something
FROM someTable
LEFT JOIN
(table1 INNER JOIN
table2 bbm
ON table2.table1_id = table1.id
)
ON table1.account_id = table2.account_id
That said, this is really arcane, because the alias renames table2 to bbm, which is not used. The ON conditions are only referring to earlier tables. The result is some strange form of CROSS JOIN.
That you can nest JOINs this way should -- in my opinion -- be merely viewed as an amusement. Don't nest JOINs. Each JOIN should be followed by its very own ON clause, before the next table/subquery reference. Nesting joins makes the code is harder to understand. It can introduce errors (which I think happened in this case). There can also be subtle edge cases where it is a little challenging to figure out what is happening.
There are two syntax variations in Standard SQL for writing multiple joins, the common one:
a join b on ... join c on ... join d on ...
and the strange one:
a join b join c join d on ... on ... on ...
In both cases the first ON is processed first, which means the strange syntax joins the first table in the last ON and the last table in the first ON, which is really hard to follow. That's why almost nobody is using it (but some tools might create it)
To change your strange
SELECT table1.something, table2.something
FROM someTable
LEFT JOIN table1
INNER JOIN table2 bbm
ON table2.table1_id = table1.id
ON table1.account_id = someTable.account_id
to the common one move the last ON after the first join:
SELECT table1.something, table2.something
FROM someTable
LEFT JOIN table1
ON table1.account_id = someTable.account_id
INNER JOIN table2 bbm
ON table2.table1_id = table1.id

How to do a join on 2 tables, but only return the data for one table?

I am not sure if this is possible. But is it possible to do a join on 2 tables, but return the data for only one of the tables. I want to join the two tables based on a condition, but I only want the data for one of the tables. Is this possible with SQL, if so how? After reading the docs, it seems that when you do a join you get the data for both tables. Thanks for any help!
You get data from both tables because join is based on "Cartesian Product" + "Selection". But after the join, you can do a "Projection" with desired columns.
SQL has an easy syntax for this:
Select t1.* --taking data just from one table
from one_table t1
inner join other_table t2
on t1.pk = t2.fk
You can chose the table through the alias: t1.* or t2.*. The symbol * means "all fields".
Also you can include where clause, order by or other join types like outer join or cross join.
A typical SQL query has multiple clauses.
The SELECT clause mentions the columns you want in your result set.
The FROM clause, which includes JOIN operations, mentions the tables from which you want to retrieve those columns.
The WHERE clause filters the result set.
The ORDER BY clause specifies the order in which the rows in your result set are presented.
There are a few other clauses like GROUP BY and LIMIT. You can read about those.
To do what you ask, select the columns you want, then mention the tables you want. Something like this.
SELECT t1.id, t1.name, t1.address
FROM t1
JOIN t2 ON t2.t1_id = t1.id
This gives you data from t1 from rows that match t2.
Pro tip: Avoid the use of SELECT *. Instead, mention the columns you want.
This would typically be done using exists (or in) if you prefer:
select t1.*
from table1 t1
where exists (select 1 from table2 t2 on t2.x = t1.y);
Although you can use join, it runs the risk of multiplying the number of rows in the result set -- if there are duplicate matches in table2. There is no danger of such duplicates using exists (or in). I also find the logic to be more natural.
If you join on 2 tables.
You can use SELECT to select the data you want
If you want to get a table of data, you can do this,just select one table date
SELECT b.title
FROM blog b
JOIN type t ON b.type_id=t.id;
If you want to get the data from two tables, you can do this,select two table date.
SELECT b.title,t.type_name
FROM blog b
JOIN type t ON b.type_id=t.id;

Cardinality violation when using a subquery that returns two values

I have create a sql query that the sketch is like this
select *
from A
where A.id in (select B.id1, B.id2 from B);
where the main select returns those values for which A.id coincides with either B.id1 or B.id2.
Clearly this solution doesn't work as the cardinality doesn't match in the where clause. How can I overcome this problem?
One solution would be to make two sub-queries, one for B.id1 and one for B.id2, but as my sub-query is much longer than in this example I was looking for a more elegant solution.
I'm using Mysql
EDIT 1
As long as the syntax is simpler than using two sub-queries I have no issues using joins
EDIT 2
Thanks #NullSoulException. I tried the first solution and works as expected!!
Something like the below should do the trick.
select *
From table1 a , (select id1 , id2 from table2 ) b
where (a.id = b.id1) or (a.id = b.id2)
or you can JOIN with the same table twice by giving the joined tables an alias.
select * from table1 a
INNER JOIN table2 b1 on a.id = b1.id1
INNER JOIN table2 b2 on a.id = b2.id2
Please test the above against your datasets/tables..

Inner Join SQL Syntax

I've never done an inner join SQL statement before, so I don't even know if this is the right thing to use, but here's my situation.
Table 1 Columns: id, course_id, unit, lesson
Table 2 Columns: id, course_id
Ultimately, I want to count the number of id's in each unit in Table 1 that are also in Table 2.
So, even though it doesn't work, maybe something like....
$sql = "SELECT table1.unit, COUNT( id ) as count, table2.id, FROM table1, table2, WHERE course_id=$im_course_id GROUP BY unit";
I'm sure the syntax of what I'm wanting to do is a complete fail. Any ideas on fixing it?
SELECT unit, COUNT( t1.id ) as count
FROM table1 as t1 inner JOIN table2 as t2
ON t1.id = t2.id
GROUP BY unit
hope this helps.
If I understand what you want (maybe you could post an example input and output?):
SELECT unit, COUNT( id ) as count
FROM table1 as t1 JOIN table2 as t2
ON t1.id = t2.id
GROUP BY unit
Okay, so there are a few things going on here. First off, commas as joins are deprecated so they may not even be supported (depending on what you are using). You should probably switch to explicitly writing inner join
Now, whenever you have any sort of join, you also need on. You need to tell sql how it should match these two tables up. The on should come right after the join, like this:
Select *
From table1 inner join table2
on table1.id = table2.id
and table1.name = table2.name
You can join on as many things as you need by using and. This means that if the primary key of one table is several columns, you can easily create a one-to-one match between tables.
Lastly, you may be having issues because of other general syntax errors in your query. A comma is used to separate different pieces of information. So in your query,
SELECT table1.unit, COUNT( id ) as count, table2.id, FROM ...
The comma at the end of the select shouldn't be there. Instead this should read
SELECT table1.unit, COUNT( id ) as count, table2.id FROM ...
This is subtle, but the sql query cannot run with the extra comma.
Another issue is with the COUNT( id ) that you have. Sql doesn't know which id to count since table1 and table2 both have ids. So, you should use either count(table1.id) or count(table2.id)

Transforming a Complicated Requirement into a SQL Query

I am having trouble with the relational algebra and transformation into SQL of this rather complicated query:
I need to select all values from table A joined to table B where there are no matching records in table B, or there are matching records but the set of matching records do not have a field that contains one of 4 of a possible 8 total values.
Database is MySQL 5.0... using an InnoDB engine for the tables.
Select
a.*
from
a
left join
b
on
a.id=b.id
where
b.id is null
or
b.field1 not in ("value1","value2","value3","value4");
I'm not sure if there is any real performance improvement but one other way is:
SELECT
*
FROM
tableA
WHERE
id NOT IN ( SELECT id FROM tableB WHERE field1 NOT IN ("value1", "value2"));
Your requirements are a bit unclear. My 1st interpretation is that you only want the A columns, and never more than 1 instance of a given A row.
select * from A where not exists (
select B.id
from B
where B.id=A.id
and B.field in ('badVal1','badVal2','badVal3','badVal4')
)
My 2nd interpretation is you want all columns from (A outer joined to B), with perhaps more than one instance of an A row if there are multiple B rows, as long as not exists B row with forbidden value.
select * from A
left outer join B on A.id=B.id
where not exists (
select C.id
from B as C
where A.id=C.id
and C.field in ('badVal1','badVal2','badVal3','badVal4')
)
Both queries could be expressed using NOT IN instead of correlated NOT EXISTS. Its hard to know which would be faster without knowing the data.