sql - inner join performing cross join - mysql

I have the following table structure.
create table t1 (
id int,
tno int
);
create table t2 (
id int,
detailno int
);
insert into t1 values (101,1);
insert into t1 values (101,2);
insert into t2 values (101,7);
insert into t2 values (101,8);
When I perform the following query:
select * from t1
inner join t2
on t1.id = t2.id
where t2.detailno = 8;
It performs a cross join and returns
id | tno | id | detailno
101 | 1 | 101 | 8
101 | 2 | 101 | 8
It is basically performing a Cross join instead of an inner join. Could you please help me return only a single result instead - since detail id = 8 is in the where clause ? I have shortened the table structure and the query for easier understanding. Here is the sql fiddle for the above code. http://sqlfiddle.com/#!9/92c98/1

Your query is doing exactly as you've asked it to do.
What is the one result you're expecting?
If you only want one line then maybe you're better off making the query more specific by adding a condition such as
WHERE t2.detailno = 8
AND t1.tno = 1
which will whittle down the results more.
Edit
If you're unsure what the value of t1.tno is going to be then could you not pass that in as a parameter? It might be more clear if you can explain why you're expecting t1.tno = 2.
You'd then potentially end up with something like this if you pass it in as a parameter.
WHERE t2.detailno = 8
AND t1.tno = #tno

Join is working as expected.
Execute this to check:
select * from t1
inner join t2
on t1.id = t2.id;
Every id is making join with another table's id and which is correct for join.
So if you want result as your expectation you need to pass t1.tno and add into where condition as there are multiple record in another table for same id (t1.id).

Related

sql: select rows which have the same column value in another column with a single query

example table:
id | value
----------
1 | abc
1 | cb3
1 | dsf
2 | sss
2 | d3
So if the input is "cb3" I want to get all rows with id 1, if the input is "sss" I want to get all rows with id 2. Can this be done with one query instead of two ? (first query is find id and second query bring rows for found id). Also, would a one query approach be faster ?
Thank you in advance.
you could try this :
SELECT *
FROM TABLE my_table
WHERE id IN (SELECT id
FROM TABLE my_table
WHERE value = input
)
Try the following, where you replace 'sss' with the value you are searching for:
select * from table t1
where t1.id in (select id from table t2 where value = 'sss')
Note that value seems not to be a key, such that you might get two different groups of ids in your result. That's also the reason why I proposed an t1.id IN (select id ... rather than an t1.id = (select id ....
You can solve it using inner join also.
SELECT S.*
FROM dataset AS S
INNER JOIN dataset AS T
ON S.id = T.id
WHERE T.value = 'cb3';

How do I create a relation between tables using an interval comparison in MySQL?

I would like to create a relationship between two tables based on whether a value in one table falls in a an interval in the other. One table 1 is ~16000 rows:
name | start | end
-----------------------------------------
someName | startPosition | endPosition
table 2 is ~20000000 rows:
id | location
--------------------------
someID | positionInteger
Each id falls in the interval of exactly one name, but each name can have many ids associated with it.
I would like to add a new index to table 2 so that it becomes:
id | location | name
---------------------------------
someID | positionInteger | someName
I've tried doing:
ALTER TABLE table2 ADD INDEX name (name);
With a bit of python I can get all the names in the database and then for each name and interval:
SELECT someID FROM table2 WHERE location >= startPosition AND location <= endPosition
Then I can loop through the resulting IDs and:
UPDATE table2 SET name = 'someName' WHERE id = 'someID'
This works but is very slow. Is there a more efficient way to do this using MySQL and avoiding multiple loops?
Join the tables
SELECT t2.id, t2.location, t1.name
FROM Table1 AS t1
JOIN Table2 AS t2 ON t2.location BETWEEN t1.startPosition AND t1.endPosition
You shouldn't be adding an index to table 2, you should be adding a column:
ALTER TABLE Table2 ADD COLUMN name VARCHAR(32); -- Replace this with the actual size
Then you can update all the rows with a similar join:
UPDATE Table2 AS t2
JOIN Table1 AS t1 ON t2.location BETWEEN t1.startPosition AND t1.endPosition
SET t2.name = t1.name
You can use LEFT JOIN :
SELECT id,location,name FROM table2 as t2
LEFT JOIN table1 as t1
ON t2.location BETWEEN t1.`start` AND t1.`end`;
More details http://sqlfiddle.com/#!9/721e5/1

Merge values in sql rows

I am looking for ways to merge row values into one row where the column to merge is the same
Transform:
FK | F1
========
3 | ABC
3 | DEF
to
FK | F1 | F2
=================
3 | ABC | DEF
Update:
I initially don`t know the values of F1. They might be everything, but I know they are unique for a given FK and they are varchars.
Update 2:
With your help I came to this query that will also add the FK for which there is only one value. I suppose it could be improved.
SELECT IFNULL(jointable.FK,table.FK) AS FK, IFNULL(jointable.F1,table.F1), jointable.F2
FROM table
LEFT JOIN
(SELECT T1.FK, T1.F1, T2.F1 AS F2
FROM table T1
LEFT JOIN table T2 ON T1.FK = T2.FK
WHERE T1.F1 <> T2.F1
GROUP BY T1.FK
) as jointable
ON table.FK=jointable.FK
GROUP BY FK;
Try this
SELECT FK
, T1.F1
, T2.F1 AS F2
FROM table T1
LEFT JOIN table T2 ON T1.FK = T2.FK AND T1.F1 <> T2.F1 --Criteria moved here
The LEFT JOIN is used since you mentioned that you have 1 or more values, which means the INNER JOIN could end up excluding rows.
The second criteria is to make sure you don't en up with rows like:
FK | F1 | F2
=================
3 | ABC | ABC
Please be aware that in case of an OUTER JOIN (either LEFT or RIGHT) the join criteria is not the same as the filter criteria, and therefore I moved it above.
In SQL Server, you can use ROW_NUMBER() over FK, maybe with an ORDER BY.
In MySQL you might be able to use it with a GROUP BY as you mentioned in comments, I am not sure it will work (at least not in SQL Server without an aggregate function or a CTE).
Here is a live test: http://ideone.com/Bu5aae
A suggestion:
SELECT FK, CONCAT(T1.F1,'',T2.F1) AS Result
FROM table T1, table T2
WHERE T1.FK = T2.FK

MySQL query, finding from one table and insert into second table

I am looking for the following:
Need the row with this condition: Common values of Device columns AND first three chars of Interface column from both tables.
Then the row which matched the above condition from Table1, retrieve the value of Specified column and store it in the Avgin column of the Table2 in the row where above condition matched.
Can someone help me with it? Database is MySQL.
If there are more than one matches, only the first one will be used.
If there are none, null will be used. If you would like something else, use ifnull().
UPDATE
table2
SET
avgin=ifnull(
(
SELECT
Specified
FROM
table1
WHERE
table1.Device=table2.Device
AND substring(table1.Interface,1,3)=substring(table2.Interface,1,3)
LIMIT 1
),
'default value'
)
edit: added the ifnull()
UPDATE with JOIN is what you need here, something like this:
UPDATE Table2 AS t2
INNER JOIN table1 AS t1 ON LEFT(t2.Interface, 3) = LEFT(t1.Interface, 3)
AND t1.Device = t2.Device
SET t2.Avgin = t1.specified;
With the JOIN condition, as you explained in your question:
LEFT(t2.Interface, 3) = LEFT(t1.Interface, 3)
AND
t1.Device = t2.Device
LEFT will give you the first 3 chars from the left of both table.
See it in action here:
SQL Fiddle Demo
This will make the table2 looks like:
| CID | DEVICE | INTERFACE | AVGIN |
---------------------------------------------------
| HDC-HKG-R01 | HDC-TBONE-P1 | P09/0/0 | 121.36 |
| OCB-OCD-R01 | OCB-PE1 | Gi5/2 | 0.17 |
| HDC-BSA-R01 | HDC-TBONE-P1 | Se9/2/0 | (null) |
Use this to confirm you're getting the rows you're expecting (ie BEFORE updating anything):
SELECT
t1.Specified
FROM
table2 t2
INNER JOIN table1 t1
ON t1.device = t2.device
AND LEFT(t1.interface,3) = LEFT(t2.interface,3)
And then, assuming that's right:
UPDATE table2 t2
INNER JOIN table1 t1
ON t1.device = t2.device
AND LEFT(t1.interface,3) = LEFT(t2.interface,3)
SET t2.Avgin = ifnull(t1.specified,'Default Value For When t1.Specified is NULL')
Note we're using an INNER join... that means that rows from table2 that have no corresponding row in table1, are discarded from the results (which is what you want).
The IFNULL will allow you to use a default value in the case when your join succeeds (because device and first three chars of interface are common to both tables), but table1.specified has a NULL value for that row.

How to delete a row in 3 tables with same query in My sql

delete from tbl_savedgroupmessage, tbl_savedusermessage, tbl_message where tbl_savedgroupmessage.msgid= tbl_savedusermessage.msgid= tbl_message.msgid= '876'
I tried this but it throws error
Try this one -
DELETE t1, t2, t3
FROM
tbl_savedgroupmessage t1
JOIN tbl_savedusermessage t2
ON t1.msgid = t2.msgid
JOIN tbl_message t3
ON t1.msgid = t3.msgid
WHERE
t3.msgid = '876'
Example:
CREATE TABLE table_a (
id INT(11) DEFAULT NULL
);
CREATE TABLE table_b (
id INT(11) DEFAULT NULL
);
INSERT INTO table_a VALUES
(2),
(3),
(1);
INSERT INTO table_b VALUES
(2),
(5),
(1);
DELETE t1, t2
FROM
table_a t1
JOIN table_b t2
ON t1.id = t2.id
WHERE
t1.id = 1;
SELECT * FROM table_a;
+------+
| id |
+------+
| 2 |
| 3 |
+------+
SELECT * FROM table_b;
+------+
| id |
+------+
| 2 |
| 5 |
+------+
You cannot delete from three tables in one query in that manner. delete is an operation that targets a single table.
delete from tbl_savedgroupmessage where msgid = '876';
delete from tbl_savedusermessage where msgid = '876';
delete from tbl_message where msgid = '876'
If there are constraints in place referencing one table to the other, you will have to change the order. If you need these all to fail or succeed together, put them in a transaction and roll back on the failure of any.
Edit: While true with ANSI compliant sql and in T-SQL, it is actually possible to delete from multiple tables in one statement in mysql. guess you learn something new every day.
You will have to join the tables together in order to delete it together. Also, add the columns you would like to delete.
for example:
delete tbl_savedgroupmessage.*, tbl_savedusermessage.*
from tbl_savedgroupmessage, tbl_savedusermessage
where tbl_savedgroupmessage.msgid = tbl_savedusermessage.msgid
and tbl_savedgroupmessage.msgid = '876'
You may use a joined DELETE command or delete the rows seperately.
DELETE FROM
tbl_message, tbl_savedgroupmessage, tbl_savedusermessage
LEFT JOIN
tbl_savedgroupmessage ON (tbl_message.msgid = tbl_savedgroupmessage.msgid)
LEFT JOIN
tbl_savedusermessage ON (tbl_message.msgid = tbl_savedusermessage.msgid)
WHERE
tbl_message.msgid = 876;