ON clause variables order - optimisation - mysql

In the 2 queries below the result set would be the same, but I was wondering if there would be any difference in speed because of the order of the comparison arguments in the ON clauses.
In the first query it matches table1.c1 = table2.c1, and the second the other way around.
SELECT * FROM table1 JOIN table2 ON (table1.c1 = table2.c1)
Or
SELECT * FROM table1 JOIN table2 ON (table2.c1 = table1.c1)

The two queries are strictly the same, there is no difference between them.
It just a convention to use the first form:
SELECT * FROM table1 JOIN table2 ON (table1.c1 = table2.c1)

Ever since the boolean expression inside ON clause returns TRUE, a record will be send to the result set output. If one of the tables returns 0 record, even if the ON clause returns TRUE, result set will be empty. So there is no difference that how you'd like to write a boolean expression inside the ON clause. Like this which means Cross Join!
Cheers
Select *
From dbo.Person As A
Inner Join
dbo.PersonOrder As B
On 1 = 1

Related

How to join a derived table

I have a complex query which results in a table which includes a time column. There are always two rows with the same time:
The result also contains a value column. The value of two rows with the same time is always different.
I now want to extend the query to join the rows with the same time together. So my thought was to join the derived table like this:
SELECT A.time, A.value AS valueA, B.value as valueB FROM
(
OLD_QUERY
) AS A INNER JOIN A AS B ON
A.time=B.time AND
A.value <> B.value;
However, the JOIN A AS B part of the query does not work. A is not recognized as the derived table. MySQL is searching for a table A in the database and does not find it.
So the question is: How can I join a derived table?
You cannot join a single reference to a table (or subquery) to itself; a subquery must be repeated.
Example: You cannot even do
SELECT A.* FROM sometable AS A INNER JOIN A ...
The A after the INNER JOIN is invalid unless you actually have a real table called A.
You can insert the subquery's results into another table, and use that; but it cannot be a true TEMPORARY table, as those cannot be joined to themselves or referenced twice at all in almost any query. _By referenced twice, I mean joined, unioned, used as an "WHERE IN" subquery when it is already referenced in the FROM.
If nothing else distinguishes the rows, you can just use aggregation to get the two values:
select time, min(value), max(value)
from (<your query here>) a
group by time;
In MySQL 8+, you can use a cte:
with a as (
<your query here>
)
select a1.time, a1.value, a2.value
from a a1 join
a a2
on a1.time = a2.time and a1.value <> a2.value;

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;

MYSQL - using AND in JOIN versus WHERE clause

If I have this query:
select * from tableA
left outer join tableB on tableA.id=tableB.id
AND tableB.foo = 1
where tableA.owner=10
I get 29 results, but if I move that AND into the WHERE clause like:
select * from tableA
left outer join tableB on tableA.id=tableB.id
where tableA.owner=10
AND tableB.foo = 1
I then get only 17 results.
I've looked all around and cannot find a definitive guide as to how using the AND differs when you use it in the JOIN versus the WHERE clause. Can anyone explain this to me?
Also, if I do something like AND tableB.foo = NULL in the JOIN all of my tableB.foo fields are NULL in the query results, even if they are not null in the table. Does having the AND in the JOIN clause change that field in the FROM selection before being filtered by the WHERE clause?
All of the criteria for the table you are outer-joining to should be in the JOIN clause (like your first query). Putting it in the WHERE clause (like your second query) implicitly converts the OUTER JOIN to an INNER JOIN.
As for your question about AND tableB.foo = NULL that is not proper MySQL syntax. NULLs require special treatment, using operators like IS NULL. You should use AND tableB.foo IS NULL instead.
An outer join joins just the same as an inner join. With the addition that when there is no match for a record, a dummy record with all columns set to null gets joined (so you still get the row from the first table in your results).
In your first query you are looking for matches in tableB with the same ID and foo = 1. For records in tableA with no such match you still get a result row (with all tableA fields null).
In the second query you are looking for matches in tableB with the same ID. For records in tableA with no such match you still get a result row (with all tableA fields null). Then in your where clause you only keep rows with foo = 1. This dismisses all outer-joined records (because their foo is null) and you are where you would have been with a plain inner join.
So always put all criteria on an outer-joined table in the ON clause. (There is one exception though; an anti join, but you can learn that pattern another time.)

Update a field thanks to a subquery based on another field

I use a MySQL DB, and I would like to update a field in a table based on another. Something like:
UPDATE table1
SET field1 = table2.id
WHERE field2 IN (
SELECT table2.name
FROM table2
);
I know that this query wouldn't work, but here is the idea. Is that even possible to do?
You can use a correlated sub query as below. This assumes there will be exactly one matching value returned. It will raise an error if more than one matching value is returned or set the field to null if zero are returned. If that last behaviour isn't desirable you will need a where clause.
UPDATE table1
SET field1 = (SELECT DISTINCT table2.ValueColumn
FROM table2
WHERE table2.JoinColumn = table1.JoinColumn)
Edit
To review records with 0 or more than 1 matches you could use
SELECT table1.JoinColumn, COUNT(DISTINCT table2.ValueColumn)
FROM table1
LEFT JOIN table2
ON table2.JoinColumn = table1.JoinColumn
GROUP BY table1.JoinColumn
HAVING COUNT(DISTINCT table2.ValueColumn) <> 1

How to join results from two tables in one output file?

I have two tables in MySQL DB; table1, table2.
Both of them have a single column (float) values. It's actually a dump from our research project which produces a single value result.
And many of these values get repeated and sorting and filtering them in Python would be cumbersome, so I though perhaps dumping them in a table in DB would be quicker.
So the end result from the SQL query is the following grouped by the value:
value table1_count table2_count
1.0 0 1
1.1 1 3
2.1 4 5
The query I am coming up with is the following:
select everything.value, everything.count1, everything.count2
from
((
select X as value, count(*) from table1
) union all (
select X as value, count (*) from table2
)) everything
group by everything.value
into outfile "/count";
Any suggestions?
Thanks,
You can't do counts by group in the inner queries, since you're defining the groups in the outer query. This should be simpler:
select everything.value, count(*)
from
(
select X as value from table1
union all
select X from table2
) everything
group by value
into outfile "/count";
Also here's some trivia: when you use UNION, you need to define column aliases only in the first query unioned.
Re your comment. Here's one solution:
select everything.value, sum(t = 'success') as s, sum(t = 'failure') as f
from
(
select X as value, 'success' as t from table1
union all
select X, 'failure' from table2
) everything
group by value
into outfile "/count";
This uses a trick in MySQL that boolean expressions return 0 for false or 1 for true. So when you sum up a bunch of expressions, you get a count of the rows where the expression is true. (Don't rely on this trick in other brands of SQL database.)