Semi-distinct union - mysql

There are UNION and UNION ALL operators in SQL. The first one drops all the duplicates, the second one doesn't. I want to drop only duplicates which originated in different subqueries, but keep those which come from the same one. Example:
TABLE t1: TABLE t2: TABLE t3:
a | b a | b a | b
————— ————— —————
1 | 2 1 | 2 1 | 3
1 | 2 1 | 3
I want (SELECT * FROM t1) UNION SEMI (SELECT * FROM t3) to return all four rows,
then (SELECT * FROM t2) UNION SEMI (SELECT * FROM t2) to return one row.
I don't really care what
(SELECT * FROM t1) UNION SEMI (SELECT * FROM t2) would return, but it would be nice if that somehow depended one the order of subqueries, e. g. in the last example it would be two rows, and in reversed (t2 UNION t1) — one.
I can do it with a huge query, but the question is — is there a standard method for such operation?
Thanks in advance.

You want a union of table1 with all the rows in table2 that don't have matching rows in table1
SELECT *
FROM table1
UNION ALL
SELECT t2.*
FROM table2 t2
LEFT JOIN table1 t1 USING (a, b, ...) -- list all the columns here
WHERE t1.a IS NULL

Related

Write row from one table into another table for each row in yet another table

I have got 3 tables: table1, table2, table3
For each row in table3 I want to take the values from table2 and table3 and insert them into table1. Do I need a loop for that? There are no keys or anything, just the 3 tables.
The columns in table2 and table3 are all present in table1 (some with different names), but table1 also has some more columns which should stay empty.
This is how the tables look in detail:
table1:
manufacturer2 | store | sku | name | title_farbe | manufacturer | meta_description_dynamic1 | title_modelljahr_prefix | title_modelljahr | size_prefix | title_gender
table2:
manufacturer2 | sku | name | title_farbe | manufacturer
table3:
store | meta
So the columns are called almost the same, except for table3.meta which should become table1.meta_description_dynamic1.
Thanks
You can do this with an INSERT ... SELECT query, where the SELECT query is a JOIN on row number between table2 and table3. Since there are extra columns in table1, you will have to enumerate the columns for each table e.g.
INSERT INTO table1 (manufacturer2, sku, name, title_farbe, manufacturer, store, meta_description_dynamic1)
SELECT manufacturer2, sku, name, title_farbe, manufacturer, store, meta
FROM (SELECT manufacturer2, sku, name, title_farbe, manufacturer,
#rownum := rownum + 1 AS rownum
FROM table2
CROSS JOIN (SELECT #rownum := 0) r
) t2
JOIN (SELECT store, meta,
#rownum2 := rownum2 + 1 AS rownum
FROM table3
CROSS JOIN (SELECT #rownum2 := 0) r
) t3 ON t3.rownum = t2.rownum
Note that ordering is not guaranteed in a SELECT with no ORDER BY clause so results may not exactly match. However for your tables you don't really have much choice as there are no common columns and no natural ordering.

mysql - Referencing alias for calculation after UNION

Ok, here's the query (pseudo-query):
SELECT *, (t1.field + t2.field) as 'result', (t1.field * t2.field) as result2 from((select as t1 limit 1) UNION ALL (select as t2 limit 1))
I need both rows returned, then do the math on the two fields into the result aliases. I know it's not graceful, but I have to kludge two queries together (the first is the union, and the second is the math)
So, how do I reference and use those two inner aliases? The inner aliases aren't accessible to the outer select.
I have a suspicion there's an obvious solution here that my brain is missing.
When you union two statements together your result is a single resultset. What you'll build:
FROM
(
(SELECT f1, f2 FROM table1 LIMIT 1)
UNION
(SELECT g1, g2 FROM table2 LIMIT 1)
) derived_table_1
This will give you a single result set named derived_table_ with two fields named f1 and f2 respectively. There will be two rows, one from your first SELECT statement and another from your second. The table aliases that you assigned inside your UNION query are no longer referencable. They exist only within their own SELECT statements.
If you have a relationship between Table1 and Table2 then you want a JOIN here:
SELECT
t1.f1 + t2.g1 as result1,
t1.f2 + t2.g2 as result2,
FROM
table1 as t1
INNER JOIN table2 as t2 ON
t1.f1 = t2.g1
If, instead no relationship exists, then you are probably looking for you original, and kludgy, union with a SUM in the SELECT:
SELECT
sum(derived_table_1.f1) as result,
sum(derived_table_1.f2) as result2
FROM
(
(SELECT f1, f2 FROM table1 LIMIT 1)
UNION
(SELECT g1, g2 FROM table2 LIMIT 1)
) derived_table_1
Editted to add a SQLFIDDLE with the last example: http://sqlfiddle.com/#!2/c8707/10
The column names or aliases for the result of a UNION are always determined by the first query. The column names or aliases defined in the subsequent queries of the union are ignored.
Demo:
mysql> create table foo ( a int, b int, c int );
mysql> insert into foo values (1,2,3);
mysql> create table bar (x int, y int, z int);
mysql> insert into bar values (4,5,6);
mysql> select a, b, c from (select a, b, c from foo union select x, y, z from bar) as t;
+------+------+------+
| a | b | c |
+------+------+------+
| 1 | 2 | 3 |
| 4 | 5 | 6 |
+------+------+------+
mysql> select x from (select a, b, c from foo union select x, y, z from bar) as t;
ERROR 1054 (42S22): Unknown column 'x' in 'field list'

MySQL - count values and merge results from multiple tables

I have two tables with one column each, containing names.
Names can have duplicates. One name can be found on every table or only in one.
I want to make an query that count duplicates, for each name in every table an list these values like this:
| name | table1 | table2 |
| john | 12 | 23 |
| mark | 2 | 5 |
| mary | | 10 |
| luke | 4 | |
I tried different strategies using UNION but no luck.
Thanks in advance!!!
SELECT DISTINCT t1.name, t1.cnt1, t2.cnt2
FROM
(SELECT name,count(name) as cnt1 FROM table1 GROUP BY name) t1
LEFT JOIN
(SELECT name,count(name) as cnt2 FROM table2 GROUP BY name) t2
ON t1.name = t2.name
UNION
SELECT DISTINCT t2.name, t1.cnt1, t2.cnt2
FROM
(SELECT name,count(name) as cnt1 FROM table1 GROUP BY name) t1
RIGHT JOIN
(SELECT name,count(name) as cnt2 FROM table2 GROUP BY name) t2
ON t1.name = t2.name
Here's a simpler solution:
You can UNION the names from the two tables together, manually differentiating their origin tables with a tbl column.
Then it's just a simple GROUP BY with conditional aggregation using the differentiating column:
SELECT a.name,
NULLIF(COUNT(CASE a.tbl WHEN 1 THEN 1 END), 0) AS table1,
NULLIF(COUNT(CASE a.tbl WHEN 2 THEN 1 END), 0) AS table2
FROM
(
SELECT name, 1 AS tbl FROM table1 UNION ALL
SELECT name, 2 FROM table2
) a
GROUP BY a.name
In accordance with your desired result-set, we NULL the count value if it turns out to be 0.
SQLFiddle Demo
SELECT SUM(res.cn), name
FROM
(
SELECT name, count(name) as cn from table1 GROUP BY name HAVING count(name) > 1
UNION ALL
SELECT name, count(name) as cn from table2 GROUP BY name HAVING count(name)>1
) as res
GROUP BY nam
e
Try the above :) I made a fiddle for you to test it:
http://sqlfiddle.com/#!3/796b2/3
It has a few double names in each table and will show you which names have doubles and then print them. The names that only appear once are not shown (acheived by the HAVING clause)
After some reading i don't think that it's posibil what i want to do. This situation ca be solved with pivot table in excel or libreoffice.
In fact this is method that i used, combined with some sql stataments to count occurence of names and export as CSV.
UNION definitetly not work. Some chance are with join, but not shure.
I found a post that discusses the same problem as mine.
MySQL - Rows to Columns

select distinct and where in so slow

table1
row_id row_one row_two
1 1 5
2 1 5
3 2 5
4 2 5
5 2 6
table2
row2_id row2_one row2_two
1 1 somevalue
2 2 somevalue2
"select distinct row_one from table1 where row_two=5"
result
row_one
1
2
after that i want select
select * from table2 where row2_one=1
select * from table2 where row2_one=2
i want select with one query.
i am trying this query
select * from table2 where row2_one in (select distinct row_one from table1 where
row_two where row_two=5)
but it took 8s
Showing rows 0 - 14 ( 15 total, Query took 8.3255 sec)
why is it so slow. i want select faster.
please help me!
You don't need the DISTINCT in there. You can just do:
SELECT *
FROM table2
WHERE row2_one IN (SELECT row_one FROM table1 WHERE row_two=5)
And using EXISTS might be faster:
SELECT *
FROM table2 A
WHERE EXISTS (SELECT * FROM table1 WHERE row_two=5 AND row_one = A.row2_one)
Let me add to the that was said bellow - use GROUP BY or EXISTS instead of DISTINCT it can really improve your performance.
Assuming that this is your query:
select *
from table2 where row2_one in (select distinct row_one from table1 where row_two=5)
Then this is well-formed. One thing, you don't need the distinct in the subquery.
If you add an index to the table1 on the column row_two, you should get better performance. An index on row2_one in table2 would also speed it up.
Select distinct table2.*
from table1 t1, table2 t2
where t1.row_two =5 and t1.row2_one = t2.row2_one

Join two tables in MySQL with random rows from the second

I have two tables
The first with only 5 rows
The second with 800 rows
I'm using this query:
SELECT *
FROM table1 t1
JOIN (SELECT * FROM table2 ORDER BY RAND() LIMIT 5) t2
But I'm getting 5 rows from the first table for each result of the second table.
I don't need a condition when joining, I just want 5 random results from the second table to join the 5 results from the first.
Example:
--------------------------------------------------------
|table1 (always with same order)| table2(random order) |
--------------------------------------------------------
item1 | item4
item2 | item2
item3 | item5
item4 | item1
item5 | item3
Do you mean UNION ?
SELECT * FROM table1
UNION SELECT * FROM table2 ORDER BY RAND() LIMIT 5;
Update: revised answer after modification of your question:
SELECT field1 FROM table1
UNION SELECT field2 FROM table2 ORDER BY RAND() LIMIT 5;
To my understanding, you just need one field from each table. If you need several ones, you can list them: field2, field2, ... as long as the number of fields is the same in both SELECTs.
Update 2: ok, I think I see what you mean now. Here is a (dirty) way to do it, I'm quite confident someone can come with a more elegant solution though:
SET #num1=0, #num2=0;
SELECT t1.field1, t2.field2
FROM (
SELECT field1, #num1:=#num1+1 AS num
FROM table1
) AS t1
INNER JOIN (
SELECT field2, #num2:=#num2+1 AS num
FROM (
SELECT field2
FROM table2
ORDER BY RAND()
LIMIT 5
) AS t
) AS t2
ON t1.num = t2.num;
Try use subquery in select. The subquery part pick an id for each row of table1.
SELECT
id AS table1_id,
(
SELECT id FROM table2 ORDER BY RAND() LIMIT 1
) AS table2_id
FROM table1
The query result would be like this:
table1_id
table2_id
1
24
2
13
3
36
4
68
5
5
You may join with table2 to select other table2 column:
SELECT table1.*, table2.*
FROM (
SELECT
id AS table1_id,
(
SELECT id FROM table2 ORDER BY RAND() LIMIT 1
) AS table2_id
FROM table1
) t
JOIN table1 on t.table1_id = table1.id
JOIN table2 on t.table2_id = table2.id