What is the difference between these two queries can anybody explain - mysql

i thought I achieve same result with the below two queries but I got a different result when I tried, can anybody please explain what is the difference, other than join and subquery.
here t1id is primary column and t2id is referenced column from tbl_1
select * from tbl_1 where t1id in (select t2id from tbl_2);
select t1.* from tbl_1 t1, tbl_2 t2 where t1.t1id = t2.t2id;
EDIT: When I tried I got 93 records for first query and 74 for second query, I changed slightly the first query like :
select * from tbl_1 where t1id in (select distinct t2id from tbl_2);
then I got 40 rows. can anybody explain whats happening.
Thanks in advance

The first query selects all rows from the first table that have an id in the second table.
The second query selects all rows from the first table, and for each of those rows, all rows in the second table. It then filters out rows where the id does not match.
If id is not unique in the second table, the second query can return more rows than the first.

They are both trying to do the same thing: get all rows from tbl_1 whose id value can be found in the t2id column if tbl_2, although the second one will return the same row from tbl_1 multiple times if the id is found multiple times in tbl_2 (not particularly useful).
But both queries are inferior to this one, which also does the sane thing, just mucho re efficiently:
select distinct t1.*
from tbl_1 t1
join tbl_2 t2 on t1.t1id = t2.t2id;

The reason for mismatch between 'in' and 'join' query is:-
tbl1
id name
1 abc
tbl2
id name
1 abc
1 abc
now, select * from tbl1 where id in (select id from tbl2)
although there exists 2 values of id from table 2 i.e. 1, but only one row is fetched from tbl1.
you can try executing the above query as
select * from tbl1 where id in (1,1) --> It will give 1 row only
This is because "In returns true if a specified value matches any value in a subquery or a list"
2nd, the innerjoin query will result in 2 rows as id from tbl1 gets matched with 2 rows from tbl2 table.

Related

Mysql Return rows which are not repeated from two tables

my question is the following:
I have two exact same tables (different names):
id, object_id, timestamp, value1, value2
I want a query which shows all the values which are in table 1 BUT not in table 2 (all rows in table 2 are in table 1).
Important note: I consider the same row when object_id, timestamp and value1 are the same (id and value2 can be different).
Thanks in advance.
Try the code below:
select a.* from table1 a
left join table2 b
on a.object_id=b.object_id and a.timestamp=b.timestamp and a.value1=b.value1
where b.object_id is null
You're creating a left join from one table to the other - so if anything appears in table1 that does not in table 2 will be included.
By adding the where condition you filter for rows where the records exist in table1 but not table2

Is it a bug? Or an error? Or a wrong query?

I have a problem about query in mysql. I made 2 tables, tables1 and table 2. In table1 there is 2 rows data, its same in table2 where it have 2 rows data, and every table have 3 fields. In this case, I want to make view and this a query :
create view point as select table2.field2 from table1, table2;
I think it will show data in table2 where it have 2 rows data. But the result is showing 4 rows data where every 2 rows is a same data.
When im trying delete 1 row in table1, its mean delete 1 data, and i try input the query like above. The result is correct, its showing 2 rows data in table2. Why it correct when in table1 have 1 row data, and didnt when table1 have 2 rows data. Please how to resolve it? Or its my wrong query.
You are using implicit cross join between these two tables.
Let's say table1 has m rows and table2 has n rows.
Then
SELECT * FROM table1,table2 will return m*n rows.
The above query is equivalent to this:
SELECT * FROM table1 CROSS JOIN table2.
If you want to get all rows from table1 and table2 keeping in mind that I don't want duplicates then you need to use UNION.
So, you might be looking for this query:
CREATE VIEW point AS
SELECT table2.field2 from table2
UNION
SELECT table1.field2 from table1
Note:
If you want to allow duplicate values in your final result set then use UNION ALL instead of UNION like below:
CREATE VIEW point AS
SELECT table2.field2 from table2
UNION ALL
SELECT table1.field2 from table1

Advanced Mysql Query to get master record if two conditions matches on different rows of child records

I was writing a mysql filter query which has a primary table and another table which holds multiple records against each record of primary table (I will call this table child).
Am trying to write a query which fetches record of primary table based on its values on child table. If the child table condition is one then I will be able to do it simply by joining, but I have 2 conditions which falls on same field.
For ex.
table 1:
id name url
1 XXX http://www.yahoo.com
2 YYY http://www.google.com
3 ZZZ http://www.bing.com
table 2:
id masterid optionvalue
1 1 2
2 1 7
3 2 7
4 2 2
5 3 2
6 3 6
My query has to return unique master records when the optionvalue matches only both 2 different conditions match on second table.
I wrote query with IN...
select * from table1
left join table2 on table1.id=table2.masterid
where table2.optionvalue IN(2,7) group by table1.id;
This gets me all 3 records because IN is basically checking 'OR', but in my case I should not get 3rd master record because it has values 2,6 (there is no 7). If I write query with 'AND' then am not getting any records...
select * from table1
left join table2 on table1.id=table2.masterid
where table2.optionvalue = 2 and table2.optionvalue = 7;
This will not return records as the and will fail as am checking different values on same column. I wanted to write a query which fetches master records which has child records with field optionvalues holds both 2 and 7 on different records.
Any help would be much appreciated.
Indeed, as AsConfused hinted, you need to two joins to TABLE2 using aliases
-- both of these are tested:
-- find t1 where it has 2 and 7 in t2
select t1.*
from table1 t1
join table2 ov2 on t1.id=ov2.masterid and ov2.optionValue=2
join table2 ov7 on t1.id=ov7.masterid and ov7.optionValue=7
-- find t1 where it has 2 and 7 in t2, and no others in t2
select t1.*, ovx.id
from table1 t1
join table2 ov2 on t1.id=ov2.masterid and ov2.optionValue=2
join table2 ov7 on t1.id=ov7.masterid and ov7.optionValue=7
LEFT OUTER JOIN table2 ovx on t1.id=ovx.masterid and ovx.optionValue not in (2,7)
WHERE ovx.id is null
You can try something like this (no performance guarantees, and assumes you only want exact matches):
select table1.* from table1 join
(select masterid, group_concat(optionvalue order by optionvalue) as opt from table2
group by masterid) table2_group on table1.id=table2_group.masterid
where table2_group.opt='2,7';
http://sqlfiddle.com/#!9/673094/9
select * from t1 where id in
(select masterid from t2 where
(t2.masterid in (select masterid from t2 where optionvalue=2))
and (t2.masterid in (select masterid from t2 where optionvalue=7)))
Old school :-) Query took 0.0009 sec.
This can also be done without the joins using correlated exists subqueries. That may be more efficient.
select *
from table1
WHERE EXISTS (SELECT 1 FROM table2 WHERE table1.id=table2.masterid and optionvalue = 2)
AND EXISTS (SELECT 1 FROM table2 WHERE table1.id=table2.masterid and optionvalue = 7)
If this is to be an exclusive match as suggested by, "when the optionvalue matches only both 2 different conditions match on second table" then you could ad yet a third exists condition. Performance-wise this may start to break down.
AND NOT EXISTS (SELECT 1 FROM table2 WHERE table1.id=table2.masterid AND optionvalue NOT IN (2,7)
Edit: A note on correlated subqueries from Which one is faster: correlated subqueries or join?.

mysql find records in a table that reference records that don't exist

In mysql, if I have a record that references the id of another record. For example
Table 1
id bigint
tabe2ref bigint
Table 2
id bigint
Where table2ref simply references Table2.id.
Is there a way to list all records in table 1 that reference a record in table 2 where that record doesn't exist?
If you want the data from table2 as well, use a LEFT JOIN as in dognose's answer. If you only want the data from table1, use a subquery, like this:
SELECT * FROM table1 WHERE table2ref NOT IN (
SELECT id FROM table2
)
Essentially, this reads "get everything from table 1 and subtract all rows which have a table2ref that isn't in all rows in table2."
You are looking for a LEFT JOIN - everything where the entry in table 2 is not existing will have a null for the table2.id after joining:
SELECT
table1.id, table1.table2ref, table2.id
FROM
table1
LEFT JOIN
table2
ON
table1.table2ref = table2.id
WHERE
ISNULL(table2.id) -- only those records with missing reference.
See also: http://giannopoulos.net/wp-content/uploads/2013/05/BHVicYICMAAdHGv.jpg
(first column, second row)

left join returning more than expected

Using the following query
select *
from table1
left join table2 on table1.name = table2.name
table1 returns 16 rows and table2 returns 35 rows.
I was expecting the above query to return 16 rows because of the left join, but it is returning 35 rows. right join also returns 35 rows
Why is this happening and how do I get it to return 16 rows?
LEFT JOIN can return multiple copies of the data from table1, if the foreign key for a row in table 1 is referenced by multiple rows in table2.
If you want it to only return 16 rows, one for each table 1 row, and with a random data set for table 2, you can use just a plain GROUP BY:
select *
from table1
left join table2 on table1.name = table2.name
group by table1.name
GROUP BY aggregates rows based on a field, so this will collapse all the table1 duplicates into one row. Generally, you specify aggregate functions to explain how the rows should collapse (for example, for a number row, you could collapse it using SUM() so the one row would be the total). If you just want one random row though, don't specify any aggregate functions. MySQL will by default just choose one row (note that this is specific to MySQL, most databases will require you to specify aggregates when you group). The way it chooses it is not technically "random", but it is not necessarily predictable to you. I guess by "random" you really just mean "any row will do".
Let's assume you have the following tables:
tbl1:
|Name |
-------
|Name1|
|Name2|
tbl2:
|Name |Value |
--------------
|Name1|Value1|
|Name1|Value2|
|Name3|Value1|
For your LEFT JOIN you'll get:
|tbl1.Name|tbl2.Name|Value |
----------------------------
|Name1 | Name1 |Value1|
|Name1 | Name1 |Value2|
|Name2 | NULL | NULL |
So, LEFT JOIN means that all records from LEFT (first) table will be returned regardless of their presence in right table.
For your question you need to specify some specific fields instead of using "*" and add GROUP BY tbl1.Name - so your query will look like
select tbl1.Name, SOME_AGGREGATE_FUNCTION(tbl2.specific_field), ...
from table1
left join table2 on table1.name = table2.name
GROUP BY tbl1.Name
One way to use this is by using the power of SQL distinct.
select distinct tbl1.id, *
from table1 tbl1
left join table2 tbl2 on tbl2.name = tbl1.name
where
....................
Please not that I am also using aliasing.
If the name column is not unique in the tables then you may simply have duplicates on table2.
Try running:
select * from table2 where name not in (select name from table1);
If you get no results back then duplicates on the name column is the reason for the extra rows coming back.
Duplication may be reason. See example in the post
https://alexpetralia.com/posts/2017/7/19/more-dangerous-subtleties-of-joins-in-sql
if you want to join the single latest/earliest relative row from right table, you can limit the join data using min/max primary key and then limiting to 1 row using group Like this:
SELECT * FROM table1
LEFT JOIN (SELECT max(tbl2_primary_col), {table2.etc} FROM table2 GROUP BY name) AS tbl2
ON table1.name = tbl2.name
WHERE {condition_for_table1}
And remember don't use * for left join because it will disable min/max and always return first row.
As per your comment "A random row from table2, as long as name from table1 matches name from table2", you can use the following:
select table1.name, (select top 1 somecolumn from table2 where table2.name = table1.name)
from table1
Note that top 1 is not mysql but it is for SQL Server