How to join a derived table - mysql

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;

Related

MySql Join tables using aggregate(min) in where condition, without subquery

I am trying to get one table, along with the lowest value of a column of another table by LEFT JOIN. I am using subquery to do this.
Sample Snippet:
SELECT *
FROM A
JOIN
(select A_id,
MIN(id) AS complete_date
from C
group by A_id) B ON (A.id=B.A_id)
WHERE A.status="complete";
Is there any possible and efficient way to achieve this without subquery and group by.
A correlated subquery -- with the right indexes -- is often the fastest approach:
SELECT A.*,
(SELECT MIN(C.id)
FROM C
WHERE A.id = C.A_id
) as complete_date
FROM A
WHERE A.status = 'complete;
This avoids the aggregation on an entire table, which is why there is a performance gain.
The index you need is on C(A_Id, id) (the second column is not as important as the first). You may also want an index on A(status).

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;

Sum all rows returning from SELECT statement

SELECT SUM(SELECT type.value
FROM type,item_type
WHERE item_type.type_id = type.id AND item_type.type_id IN (4,7)
GROUP BY type.id)
What's wrong with this query? I would like to sum all rows coming from the internal query.
You can't use SUM() function with subquerys. According to the manual the SUM() function returns the total sum of a numeric column. The way you were doing, was something like that: SELECT 1+50+30+10. Where is the table you were selecting the values? The sintax is:
SELECT SUM(column) FROM table
Take a look at:
http://www.w3schools.com/sql/sql_func_sum.asp
The correct way is
SELECT t.id, SUM(t.value)
FROM type as t,
INNER JOIN item_type as it
ON it.type_id = t.id
WHERE it.type_id IN (4,7)
GROUP BY t.id
Consider to use JOIN sintax instead of multiple tables: SQL left join vs multiple tables on FROM line?
You should learn to use proper join syntax and table aliases:
SELECT SUM(t.value)
FROM type t JOIN
item_type it
ON it.type_id = t.id
WHERE it.type_id IN (4,7);
If you want one row for each type.id, then you need a GROUP BY.
Your query doesn't work because subqueries are not allowed as arguments to aggregation functions. Even if they were, the context would be for a scalar subquery and your subquery is likely to return more than one row.
Just use SUM without internal query :
SELECT type.id, SUM(type.value)
FROM type,item_type
WHERE item_type.type_id = type.id AND item_type.type_id IN (4,7)
GROUP BY type.id

Join 2 Tables When Table2 Doesn't Have Records Relating to Table1

I have two tables, call them Table1 and Table2. Table1 has a primary key of "ID" and Table2 has a foreign key field called "Table1ID".
I can run this join, but it will only work the way I want it to when there is a matching primary and foreign key value in both tables.
SELECT a.*, sum(b.Time) AS Time FROM Table1 AS a JOIN Table2 AS b ON a.ID = b.Table1ID
As you can see, I'm trying to pull all fields from Table1 and a sum of the field "Time" in Table2, where the primary and foreign keys match.
If there isn't a foreign key, I still want the record from Table1 to display, the "Time" field should simply show a 0.
First it sounds like you need to use aggregation since you're trying to sum the times for a given id. Use group by for that and define the fields as needed (vs *).
Second, you need to use an outer join to return those records that don't have matches.
Finally, you can use coalesce to convert null sums to 0:
SELECT a.id,
coalesce(sum(b.Time),0) AS Time
FROM Table1 AS a
LEFT JOIN Table2 AS b ON a.ID = b.Table1ID
GROUP BY a.id
SQL Fiddle Demo
Your query as written would not execute because you are not allowed to do an aggregate function in the select clause if other fields are present that are not in a group by clause. So a couple of options are:
Include every field in Table1 into your select and group by clauses such as:
SELECT a.ID, a.Attribute1, a.Attribute2, a.Attribute3...
, coalesce(sum(b.Time), 0) AS Time
FROM Table1 AS a
LEFT JOIN Table2 AS b
ON a.ID = b.Table1ID
Group by a.ID, a.Attribute1, a.Attribute2, a.Attribute3
Or you can create a sub-query that does the aggregation on on Table2 that is then joined with Table1.
SELECT a.*
, coalesce(b.Time, 0) AS Time
FROM Table1 AS a
LEFT JOIN
(
SELECT Table1ID, SUM(Time) Time
FROM Table2
GROUP BY Table1ID
) AS b
ON a.ID = b.Table1ID
Note that you need a LEFT JOIN, which means that every record in the first table (Table1) is returned whether or not there is a matching record in the second table. If you don't want a null in your results if there isn't a match, then you add the coalesce function to turn any nulls to zeros.

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.