How do I join these two tables together?
type count
NULL 117
2 1
type count
NULL 807
1 3
2 32
I've tried INNER JOIN, LEFT JOIN, and RIGHT JOIN and I can't quite figure it out. I would like the end result to look like
type count count
NULL 117 807
1 NULL 3
2 1 32
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
Sourced from this question
You have a problem because NULLs do not match by default. But, you can make them match. Let me assume the first is t1 and the second t2
select t2.type, t1.count, t2.count
from t2 left join
t1
on t2.type = t1.type or (t2.type is null and t1.type is null);
Here is a SQL Fiddle that demonstrates that this correctly answers the OP's question.
The right type of join would be FULL OUTER JOIN, but it does not exist in MySQL.
We can emulate it in the following way:
SELECT t1.type, t1.count, t2.count # exact matches
FROM t1 join t2 on t1.type = t2.type
UNION
select t1.type, t1.count, NULL # t2 not present
FROM t1 left join t2 on t1.type = t2.type
WHERE t2.type IS NULL
UNION
select t2.type, NULL, t2.count # t1 not present
FROM t2 left join t1 on t2.type = t1.type
WHERE t1.type IS NULL
UNION
select NULL, t1.count, t2.count # NULL on both sides
FROM t1, t2
WHERE t1.type IS NULL and t2.type IS NULL;
I have a question for the question ;)
What would happen if the tables would look like follows? (just a small change)
type count
NULL 117
3 333
2 1
type count
NULL 807
1 3
2 32
Because in this case both tables contain records that do not match the other table, so probably joining from one direction is not enough and you need to join the tables from both directions, but then, you might have trouble using data for the 'type' from only one table...
So one solution might be something like:
select if (t1.type is null, t2.type, t1.type) as type, t1.count count1, t2.count count2
from t1
left join t2
on t1.type=t2.type or (t1.type is NULL and t2.type is NULL)
union
select if (t1.type is null, t2.type, t1.type) as type, t1.count count1, t2.count count2
from t1
right join t2
on t1.type=t2.type or (t1.type is NULL and t2.type is NULL);
In addition,
you may also use the coalesce() function instead of if (.. is null, ...) e.g. coalesce(t1.type, t2.type)
you may still need to be careful with union, perhaps you want to keep duplicated records (if there is any) and use union all
http://www.sqlfiddle.com/#!2/302e69/2
Related
I have 2 access tables that contain partially similar data, one being more enriched than the other.
The idea here is to join the two tables by the fields id and num and to get from the table T2 the num that are not in the table T1
T1:
id
num
1
34
3
51
7
23
T2:
id
num
status
1
34
done
1
79
done
1
39
done
3
51
done
7
23
done
Expected result:
id
num
status
1
79
done
1
39
done
Under access I read on internet that there is no MINUS operator like under MySQL, so I tried with EXCEPT but the query takes a long time (stopped after 10min)
So I tried this:
SELECT T2.*
FROM T2 LEFT JOIN T1 ON (T1.id =T2.id)
WHERE T1.num IS NULL AND ( (T2.status LIKE 'done') );
The problem now is, I don't have all the records that are in T2 but not in T1
You can use RIGHT JOIN. And I recommend to do not use "LIKE" in this case because this is slow. You can just use the = operator. So your query will be:
SELECT t2.id, t2.num, t2.status
FROM t1
RIGHT JOIN t2
ON t1.id = t2.id
AND t1.num = t2.num
WHERE t1.num IS NULL
AND t2.status = 'done';
In case all column names you want to join are identic in both tables, you can join more simple:
SELECT t2.id, t2.num, t2.status
FROM t1
RIGHT JOIN t2
USING (id,num)
WHERE t1.num IS NULL
AND t2.status = 'done';
I don't like this, but it's shorter. At the end, your concrete query depends on your personal "taste".
There is a lot of variants.
SELECT t2.*
FROM t2
LEFT JOIN t1 USING (id, num)
WHERE t1.id IS NULL
AND t2.status = 'done'
SELECT *
FROM t2
WHERE NOT EXISTS ( SELECT NULL
FROM t1
WHERE (t1.id, t1.num) = (t2.id, t2.num) )
AND status = 'done'
There are more variants...
What of these (or maybe some another) variants is the most effective? this depends on the tables definitions and data statistic.
You're missing a condition on the join:
SELECT T2.*
FROM T2
LEFT JOIN T1
ON T1.id = T2.id
AND T1.num = T2.num
WHERE T1.num IS NULL
AND T2.status LIKE 'done';
Check it here.
SELECT t1.id, t1.name, t1.population, CAST(SUM(t2.town_1) AS CHAR) AS town, CAST(SUM(CASE WHEN t2.id LIKE 23 THEN 1 ELSE 0 END) AS CHAR) AS population FROM area1 t1 LEFT JOIN area2 t2 ON t1.id = t2.id WHERE t1.id like 23
Normally it will match if id = 23 exist in both tables. But it's not the case (not in t1 table) so the request return NULL values.How i can do to return no result?
If you only want the result to be matched if the ID exists in BOTH tables, you're looking for an INNER JOIN instead of a LEFT JOIN.
EDIT:
On second thought, it's because of your aggregate functions (SUM). You're casting them as CHARs (didn't even know you could do that). You should be using a GROUP BY. Try this:
SQL Fiddle
SELECT
t1.id,
t1.name,
t1.population,
SUM(t2.town_1) AS town,
SUM(CASE WHEN t2.id LIKE 23 THEN 1 ELSE 0 END) AS population
FROM
area1 t1
INNER JOIN area2 t2
ON t1.id = t2.id
WHERE
t1.id like 23
GROUP BY
t1.id,
t1.name,
t1.population
On a side note, there's no need for the like in your WHERE clause. The way you have it, it's doing the same thing as an = sign.
Ok. I have some data in one table, that references on multiple occasions some data in another table.
Table1 - main client table
Table2 - user defined fields
Say I have a query that shows a client id from Table1 and all attached / used "used defined fields" from Table2
SELECT t1.Id, t2.udf
FROM Table1 t1
JOIN Table2 t2 ON t1.Id = t2.Index
WHERE t1.EndDate IS NULL AND
t1.Id = '1234.9876' AND
I would get the following for a result...
ID UDF
1234.9876 100
1234.9876 110
1234.9876 118
1234.9876 124
1234.9876 198
1234.9876 256
Now, say I wanted to query this same thing, and get ONLY the ID of the Client, but ONLY IF a value for t2.udf equaling '194' did not exist. So, I would simply get
ID
1234.9876
...as a result.
Make the join a LEFT join and filer where t2.Index is null
SELECT t1.Id
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.Id = t2.Index
AND t2.UDF = 194 -- has to be before where clause
WHERE t2.Index IS NULL
AND t1.EndDate IS NULL
AND t1.Id = '1234.9876' -- not sure if you want this part
Another way by using NOT EXISTS
SELECT t1.Id
FROM Table1 t1
WHERE NOT EXISTS (SELECT 1 FROM Table2 t2 WHERE t1.Id = t2.INDEX
AND t2.UDF = 194)
AND t1.EndDate IS NULL
AND t1.Id = '1234.9876'
See also JOINS
You can add AND t2.udf not in (select udf from table2 where udf <> '194').
But #SQLMenace solution is better
This should do it.
SELECT DISTINCT t1.Id
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.Id = t2.Index
WHERE t2.UDF NOT IN (194)
AND t2.Index IS NULL
Select DISTINCT gives you unique entries that satisfy the other conditions, and the first where clause
t2.UDF NOT IN (194)
Normall would return all the rows for the t1 where the t2.UDF is not 194, but it is limited by the Select Distinct to give you only distinct id's
Try the following:
SELECT t1.Id
FROM Table1 t1
JOIN Table2 t2 ON t1.Id = t2.Index
WHERE t1.EndDate IS NULL AND
t1.Id = '1234.9876' AND
t2.udf <> '194'
I have the 2 following tables t1, t2 with values,
t1 t2
1 4
2 2
3 3
Now I want to output
1
4
How can I get this output in select query ?
This will get you each item from t1 that is not present in t2, and each item in t2 that is not present in t1:
select t1.id from t1
left join t2 on t2.id = t1.id
where t2.id is null
union all
select t2.id from t2
left join t1 on t1.id = t2.id
where t1.id is null
(I have assumed that the field name in each table is named id just for the sake of being able to write a query against the tables.)
Another way would be:
select coalesce(t1.id, t2.id)
from t1
full outer join t2 on t2.id = t1.id
where t1.id is null or t2.id is null
Another way. Just COUNT them.
This works if the values are unique per table
SELECT
CombinedValue
FROM
(
SELECT t1 AS CombinedValue FROM t1
UNION ALL
SELECT t2 FROM t2
) foo
GROUP BY
CombinedValue
HAVING
COUNT(*) = 1
If not unique per table
SELECT
CombinedValue
FROM
(
SELECT DISTINCT t1 AS CombinedValue FROM t1
UNION ALL
SELECT DISTINCT t2 FROM t2
) foo
GROUP BY
CombinedValue
HAVING
COUNT(*) = 1
you can use Joins in MySql to proceed and to obtain result.
this will help you
http://www.techrepublic.com/article/sql-basics-query-multiple-tables/1050307
SELECT AVG(`col5`)
FROM `table1`
WHERE `id` NOT IN (
SELECT `id` FROM `table2`
WHERE `col4` = 5
)
group by `col2` having sum(`col3`) > 0
UNION
SELECT MAX(`col5`)
FROM `table1`
WHERE `id` NOT IN (
SELECT `id` FROM `table2`
WHERE `col4` = 5
)
group by `col2` having sum(`col3`) = 0
For readability and performance reasons, I think this code could be refactored. But how?
EDITIONS
removed the outer select
made the first select to return a sum and the second one to return another value
replaced the SUM by AVG
SELECT *
FROM table1 t1
left outer join table2 t2 on t1.id = t2.id and t2.col4 = 5
where t2.id is null
group by t1.col2
having sum(col3) >= 0
The outer select is missing the FROM clause and was not adding anything so I removed it. The NOT IN is inefficient compared to the LEFT OUTER JOIN method so I replaced that. The two UNIONs were easily combined into one by using >=.
Update:
Note the use of UNION ALL rather than UNION. I don't think you want to remove duplicates, and it will perform faster this way.
SELECT AVG(t1.col5)
FROM table1 t1
left outer join table2 t2 on t1.id = t2.id and t2.col4 = 5
where t2.id is null
group by t1.col2
having sum(t1.col3) > 0
UNION ALL
SELECT MAX(t1.col5)
FROM table1 t1
left outer join table2 t2 on t1.id = t2.id and t2.col4 = 5
where t2.id is null
group by t1.col2
having sum(t1.col3) = 0
SELECT t1.* FROM table1 t1 LEFT OUTER JOIN table2 t2 ON t1.id = t2.id
WHERE t2.col4 <> 5 AND SUM(t1.col3) > 0 GROUP BY t1.col2
I'm guessing that you want this:
SELECT * FROM `table`
WHERE col2 IN
(SELECT col2
FROM `table1`
WHERE `id` NOT IN (
SELECT `id` FROM `table2`
WHERE `col4` = 5
)
group by `col2` having sum(`col3`) >= 0
)
When using GROUP BY, you should only return columns that are named in the GROUP BY clause or that include an aggregate function. Therefore, the inner SELECT here gets the col2 values where the sum is greater than or equal to zero, then the outer SELECT grabs the entire row for those values.
SELECT
IF(SUM(`table1`.`col3`) > 0, AVG(`table1`.`col5`), MAX(`table1`.`col5`))
FROM `table1`
LEFT JOIN `table2` ON `table2`.`id` = `table1`.`id` AND `table2`.`col4` = 5
WHERE `table2`.`id` IS NULL
GROUP BY `table1`.`col2`
HAVING SUM(`table1`.`col3`) >= 0
Also * is considered harmful. If you want to make your query forward-compatible with possible future changes to your DB model, specify columns by their names.