mysql query join/sub-query/union - mysql

Say I've two tables - "Table1" and "Table2" in my MySQL database.
"id" primary key (auto_increment) in "Table1" is the reference key in "Table2" - "tab_id".
There could be zero or more "Table2" rows for one "Table1" row.
Now I'm trying to do a search on one of the column in "Table2" say "email" column OR on one of the column in "Table1" say "address" and print "Table1" row values.
I see there are 3 possibilities:
1. Join
2. Sub-Query
3. Union
1 Join
SELECT *
FROM Table1 t1, Table t2
WHERE t1.id = t2.tab_id
AND (t1.address like '%str%' OR t2.email like '%str%');
-- This works fine, but when there are no rows in "Table2" relevant to "Table1" .. the JOIN will fail, hence output is in-consistent.
2 Sub-Query
SELECT *
FROM Table1 t1
WHERE t1.address like '%str%'
OR t1.id IN (SELECT t2.tab_id
FROM Table2 t2
WHERE t2.email like '%str%');
-- This works fine, but when there are two manys rows in "Table2" (say 5K) the query goes very slow :(
3 Union
SELECT 'relevant_columns'
FROM Table1 t1, Table t2
WHERE t1.id = t2.tab_id
AND (t1.address like '%str%' OR t2.email like '%str%')
UNION
SELECT 'relevant_columns'
FROM Table1 t1
WHERE t1.address like '%str%'
ORDER BY relevant_column
-- This works fine, may be create a view with a similar UNION, does the job.
Now, my question what is the correct way ... is it okay to call a UNION always?
MySQL Engine: MyISAM

SELECT *
FROM Table1 t1
LEFT JOIN Table t2 ON t2.tab_id = t1.id
WHERE t1.address like '%str%'
OR t2.email like '%str%';
You need to do a LEFT JOIN. When you make a FROM from two tables as you did, it works as an INNER JOIN (or a CROSS JOIN if there is no WHERE clause), which means that the output shows only rows that have a match in both tables. With LEFT JOIN you said that you want all rows from the left table (t1) with the matched row on the right table (t2). If there is no match in t2, then null is used.
You can use sub-query, but as you can see it is not the best choice
An UNION here does not give you any advantage. An UNION is useful to merge together datasets with same columns.
Edit
If you have issues with JOIN, because some Table1 rows do not appear, then you need a LEFT JOIN. The fact that takes a long time, is another problem. Those tables are not big at all, so I guess you need to do some index work on those tables.
If you want help about the union you need to tell me which are those relevant_columns, because they must have the same number of columns, same type and same sequence.
You might optimize the union without joins, depending on what you want to output when t2.email has a match. Here is an example
SELECT t1.id, t1.address, null as email
FROM Table1 t1
WHERE t1.address like '%str%'
union
SELECT t2.tab_id as id, null as address, t2.email
FROM Table t2
WHERE t2.email like '%str%';

SELECT *
FROM Table1 t1
LEFT JOIN Table2 t2
ON t1.id = t2.tab_id
WHERE t1.address like '%str%' OR t2.email like '%str%';

Related

SQL query gives no results when one of the tables is empty

I'm trying to perform a SQL query like this:
SELECT
t1.*
FROM
`table1` t1,
`table2` t2
WHERE
t1.x = 1
and the table called table2 (t2) is empty but inside t1 there are entries.
For example this query works perfect:
SELECT
t1.*
FROM
`table1` t1
WHERE
t1.x = 1
So just by adding the second table t2 in the FROM part the query gives no results anymore. So I don't understand what is going on here. In my case it should be possible that one of the tables is empty but the query still needs to give results. How can I achieve this?
Your syntax is using an implied JOIN between table1 and table2, and could be rewritten as:
SELECT t1.*
FROM `table1` t1,
CROSS JOIN `table2` t2
WHERE t1.x = 1
This is JOINing everything in table1 against everything in table2. However, as table2 is empty there is nothing to join to.
With an implied CROSS JOIN, results are only returned when the row on both sides of the join is matched, which in this case it cannot be. Therefore, nothing is returned.
If you rewrote the statement to use a LEFT JOIN, you would see all results from table1, and only matching results from table2:
SELECT t1.*
FROM `table1` t1,
LEFT JOIN `table2` t2 ON 1 = 1
WHERE t1.x = 1
Incidentally, typically you would only use this kind of query if there is a relationship in the data between table1 and table2. In this case, you would JOIN on the related columns, like so:
SELECT t1.*
FROM `table1` t1,
LEFT JOIN `table2` t2 ON t2.matchedColumn = t1.matchedColumn
WHERE t1.x = 1

Multi-column join very slow in MySQL

I have two large tables, table_1 and table_2. Each has columns id, name and number. I want to find the records in table_1 that have matching names and numbers in table_2:
select t1.* from table_1 t1, table_2 t2
where t1.name = t2.name and t1.number = t2.number;
Or using the join syntax:
select t1.* from table_1 join table_2 on
t1.name = t2.name and t1.number = t2.number;
Both are very slow.
I added indexes for name and number in both tables. Still very slow. I then added a multi column index (name, number) on both tables. Still very slow.
Finally, I tried creating a combined column in both tables and set its value to concat(name, number). I added indexes in both tables for the combined column and then ran the following:
select t1.* from table_1 t1, table_2 t2
where t1.combined = t2.combined
Super fast!
Isn't there a more elegant way to achieve the performance without the awkward workaround of creating the combined column?

SQL - show results from table without values from another table

I need to show only results which are in Table1 and Table2 but are not in Table3. Basically, it should be something like TABLE1, Table2 except INNER JOIN between (TABLE1, Table2) and TABLE3.
Should looks like this - On left side Table1 and Table2, on right side Table3
Now I have this:
SELECT mesta_email, mesta_kod
FROM Table1
UNION ALL
SELECT mesta_email, mesta_kod
FROM Table2
// And somehow except values which are in Table3
Can somebody help me please? Thanks a lot.
There are a couple different ways to do this. I believe mysql does better with the outer join/null approach:
select t.*
from (
SELECT mesta_email, mesta_kod
FROM Table1
UNION ALL
SELECT mesta_email, mesta_kod
FROM Table2
) t left join Table3 t3 on t.mesta_email = t3.mesta_email
and t.mesta_kod = t3.mesta_kod
where t3.mesta_email is null
This assume table3 shares the same structure as the other 2 tables.
I would approach the problem almost directly as you write it, using exists and not exists:
select t1.mesta_email, t2.mesta_kod
from table1 t1
where exists (select 1
from table2 t2
where t2.mesta_email = t1.mesta_email and t2.mesta_kod = t1.mesta_kod
) and
not exists (select 1
from table3 t3
where t3.mesta_email = t1.mesta_email and t3.mesta_kod = t1.mesta_kod
);
One advantage of exists/not exists over other approaches involves duplicates. If one of the tables (say table1) has not duplicates, but the others might, there is no need to remove duplicates in the resulting data set.

join all columns in table1 to columns with "unique names" in table2

I am using mySQL 5.6. I have two tables: t1 and t2. Both have many columns. And many columns in t1 and t2 share the same name: for example, there is a "var1" column in t1 and in t2.
I want to join the tables, selecting (a) all columns from t1 and (b) only the columns in t2 that have names that don't appear in t1. For example, I would not select "var1" from t2.
Here is a valid mySQL command that does not work because some columns share the same name:
SELECT * FROM t1 LEFT JOIN t2 ON (t1.ID=t2.ID);
MySQL sensibly returns this error message:
ERROR 1060 (42S21): Duplicate column name 'var1'
So I want to run a command like
SELECT t1.*, DISTINCTCOLUMNS(t2.*) FROM t1 LEFT JOIN t2 ON (t1.ID=t2.ID);
Except, of course, that there is no DISTINCTCOLUMNS operation in SQL. But is there a similar (real) command that will achieve the same effect?
I see that common advice is to avoid SELECT * syntax, partly for efficiency purposes. I appreciate that, but I do not want to write out the names of all of the columns that I need.
The real issue here is you have the same column name for both tables. So you need to alias your columns.. This is also why you shouldn't just pull all columns out but the specific ones you need..
SELECT t1.ID as t1_id,
t1.var1 as t1_var1,
t2.ID as t2_id,
t2.var1 as t2_var1,
... Etc.
FROM t1
LEFT JOIN t2 on t1.ID = t2.ID
With two ID columns and no way to distinguish between the two an error will occur
You could qualify the columns like
SELECT t1.*, t2.* FROM t1 LEFT JOIN t2 ON (t1.ID=t2.ID);
But you should not do it unless the columns with identical names do actually hold/reference the same data.
try like following. this may help you
SELECT t1.*, t2.YourDesiredColumnName FROM t1 LEFT JOIN t2 ON (t1.ID=t2.ID);

Join two tables on two columns, even if table 2 does not have row

What I am trying to do is join two tables, lets call them t1 and t2 on two columns. id and name, for this example. t1 will always have id and name, but t2 won't always have id and name. t1 has more columns like viewes, reports, and t2 has other columns that need to be joined. My question is, how can I show 0's for t2's columns if they don't exist?
I hav something similar to this, that joins tables only if both tables' rows have some value.
SELECT
date(t1.start_time) date,
t1.name,
t1.viewes,
t1.reports,
t2.col5,
t2.col6
from
table1 t1
left outer join table2 t2
on t2.name = t1.name and date(t2.start_time) = date(t1.start_time)
group by
1,2
order by
1 desc,
2 asc
;
I have lot's of experience with MySQL, but sometimes find that things need to be hacked to work correctly. What's your suggestion for this problem?