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
Related
Let's say I have the following table called Email, where Id is the primary key:
+----+------------------+
| Id | Email |
+----+------------------+
| 1 | anne#example.com |
| 2 | cat#example.com |
| 3 | anne#example.com |
+----+------------------+
I'm trying to delete all occurrences of duplicates except the first. So in this case the desired output would be
+----+------------------+
| Id | Email |
+----+------------------+
| 1 | anne#example.com |
| 2 | cat#example.com |
+----+------------------+
After asking a friend, I found this solution works:
DELETE t1 FROM Person t1 INNER JOIN Person t2
Where t1.Email=t2.Email and t1.Id > t2.Id
My question is why does this work? In particular, when t1 inner joins t2 on Email field, how does the program know which row of anne#example.com should be matched with which, since there are multiple occurrences of this value with different Ids?
Consider this select statement only filtering by equality among email columns
SELECT t1.*, t2.*
FROM Person t1
INNER JOIN Person t2
WHERE t1.Email=t2.Email
ORDER BY t1.Id, t2.Id;
returns (1,1), (1,3), (3,1), (3,3) for t1.id and t2.id values respectively for the mail anne#example.com, and only (2,2) for cat#example.com. Then If you consider the other filter AND t1.Id > t2.Id,
SELECT t1.*, t2.*
FROM Person t1
INNER JOIN Person t2
WHERE t1.Email=t2.Email
AND t1.id > t2.id
ORDER BY t1.Id, t2.Id;
then you'll only have one tuple (3,1) since t1.id > t2.id is satisfied only for this case of id tuples. If you convert SELECT t1.*, t2.* to DELETE t1 (of course remove ORDER BY part also), then obviously you'll delete id = 3 and left rows with id values 1 and 2, reversely if you replace SELECT t1.*, t2.* with DELETE t2, then you'll have rows with id values 2 and 3.
Demo
First, this is more commonly written using aggregation:
DELETE p
FROM Person p INNER JOIN
(SELECT p2.email, MIN(p2.id) as min_id
FROM Person p2
GROUP BY p2.email
) p2
ON p.email = p2.email and p.id > p2.min_id;
Why does your version work? Well, it works because of the fact that a join not only matches data but also filters data.
So, the condition
t1.Email = t2.Email and t1.Id > t2.Id
Says that for each record in t1 find matching records in t2 where t1.id > t2.id. That is, find records in t1 that have a matching record with a smaller id.
All records have this property -- except for one for each email. That would be the record with the smallest id.
I do not recommend this method for identifying the smallest record, because the join multiplies the number of records. If one email has five records, then there are up to four matches for one of the records. MySQL needs to figure out what to do when you say to delete a single record four times. (It does the right thing, of course, but there is extra work.)
The aggregation method doesn't have any issues like this.
You compare two identical tables and check all occurrences where emailaddress of both tables are identical.
if the id is the same, the row is ignored.
If the id is different and it must have an id that is bigger than the id of first occurrence , this row gets deleted.
I have two tables I'm trying to join to produce a unique set of data for a third table, but having trouble doing this properly.
The left table has an id field, as well as a common join field (a).
The right table has the common join field (a), and another distinct field (b)
I'm trying to extract a result-set of id and b, where neither id nor b are duplicated.
I have an SQL fiddle set up: http://www.sqlfiddle.com/#!9/208de/3/0
The ideal results should be:
id | b
---+---
1 | 1
2 | 2
3 | 3
Each id and b value appears only once (it's only coincidence they match here, that can't be assumed always).
Thanks
What about a CTE along with a DISTINCT, Would that work?
WITH
cte1 (ID, B)
AS
(
SELECT DISTINCT Table1.ID
FROM Table1
WHERE Table1.ID IS NOT NULL
GROUP BY Table1.ID
)
SELECT DISTINCT
Table2.b
FROM Table2 AS sp
INNER JOIN cte1 AS ts
ON sp.b <> ts.ID
ORDER BY ts.ID DESC
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).
I am having a tough time to understand why does LEFT JOIN / IS NULL eliminate records which are there in one table and not in the other.
Here is an example
SELECT l.id, l.value
FROM t_left l
LEFT JOIN t_right r
ON r.value = l.value
WHERE r.value IS NULL
Why should r.value = NULL eliminate records ? I am not understanding . I know I am missing something very basic but at present I cant figure out even that basic one. I would appreciate if someone explains it to me in detail .
I want a very basic explanation.
This could be explained with the following
mysql> select * from table1 ;
+------+------+
| id | val |
+------+------+
| 1 | 10 |
| 2 | 30 |
| 3 | 40 |
+------+------+
3 rows in set (0.00 sec)
mysql> select * from table2 ;
+------+------+
| id | t1id |
+------+------+
| 1 | 1 |
| 2 | 2 |
+------+------+
2 rows in set (0.00 sec)
Here table1.id <-> table2.t1id
Now when we do a left join with the joining key and if the left table is table1 then it will get all the data from table1 and in non-matching record on table2 will be set to null
mysql> select t1.* , t2.t1id from table1 t1
left join table2 t2 on t2.t1id = t1.id ;
+------+------+------+
| id | val | t1id |
+------+------+------+
| 1 | 10 | 1 |
| 2 | 30 | 2 |
| 3 | 40 | NULL |
+------+------+------+
3 rows in set (0.00 sec)
See that table1.id = 3 does not have a value in table2 so its set as null
When you apply the where condition it will do further filtering
mysql> select t1.* , t2.t1id from table1 t1
left join table2 t2 on t2.t1id = t1.id where t2.t1id is null;
+------+------+------+
| id | val | t1id |
+------+------+------+
| 3 | 40 | NULL |
+------+------+------+
1 row in set (0.00 sec)
Let's assume r-table is employees, and r_table is computers. Some employees don't have computers. Some computers are not assigned to anyone yet.
Inner join:
SELECT l.*, r.*
FROM employees l
JOIN computers r
ON r.id = l.comp_id
gives you the list of all employees who HAVE a computer, and the info about computer assigned for each of them. The employees without a computer will NOT appear on this list.
Left join:
SELECT l.*, r.*
FROM employees l
LEFT JOIN computers r
ON r.id = l.comp_id
gives you the list of ALL employees. The employees with computer will show the computer info. The employees without computers will appear with NULLs instead of computer info.
Finally
SELECT l.*, r.*
FROM employees l
LEFT JOIN computers r
ON r.id = l.comp_id
WHERE r.id IS NULL
Left join with the WHERE clause will start with the same list as the left join (2), but then it will keep only those employees that do not have corresponding information in the computer table, that is, employees without the computers.
I this case, selecting anything from the r table will be just nulls, so you can leave those fields out and select only stuff from the l table:
SELECT l.*
FROM ...
Try this sequence of selects and observe the output. Each next step builds on the previous one.
Please let me know if this explanation is understandable, or you'd like me to elaborate some more.
EDITED TO ADD:
Here's sample code to create the two tables used above:
CREATE TABLE employees
( id INT NOT NULL PRIMARY KEY,
name VARCHAR(20),
comp_id INT);
INSERT INTO employees (id, name, comp_id) VALUES (1, 'Becky', 1);
INSERT INTO employees (id, name, comp_id) VALUES (2, 'Anne', 7);
INSERT INTO employees (id, name, comp_id) VALUES (3, 'John', 3);
INSERT INTO employees (id, name) VALUES (4, 'Bob');
CREATE TABLE computers
( id INT NOT NULL PRIMARY KEY,
os VARCHAR(20) );
INSERT INTO computers (id, os) VALUES (1,'Windows 7');
INSERT INTO computers (id, os) VALUES (2,'Windows XP');
INSERT INTO computers (id, os) VALUES (3,'Unix');
INSERT INTO computers (id, os) VALUES (4,'Windows 7');
There are 4 employees. Becky and John have computers. Anne and Bob do not have a computer. (Anne, has a comp_id 7, which doesn't correspond to any row in computers table - so, she doesn't really have a computer.)
the left returns all the row from the left table
ON r.value = l.value
if there is no r.value for a l.value then the r is empty
r.value is null will be true
in your example, the result would be EVERY record from t_left, as well as every matching record from t_right where they both have the same value. where there is no match, NULL values are given instead of values from t_right. where r.value is null simply eliminates the rows that did match, by detecting those null values.
essentially saying 'give me the rows from t_left that DONT match rows in t_right
a right join performs the reverse order of the left join.
This is based on the difference between INNER JOIN and LEFT JOIN. When you use an INNER JOIN, the result only contains rows that match between the two tables (based on the ON condition).
But when you use a LEFT JOIN, you also get rows in the result for all rows in the first table that don't have a match in the second table. In these cases, all the result columns from the second table are filled with NULL as a placeholder.
The WHERE r.value IS NULL test then matches these rows. So the final result only contains the rows with no match.
LEFT JOIN takes the values on the left and tries to match the table on the left by the column specified on ON if it doesn't find a match then it interprets the right table's columns as NULL. hence only those that can't find a match on the right table will be selected by IS NULL operator
Firstly a left join states that all records from the left table will be include, and only those from the right that match.
So lets say you have a red, blue and green sock in your drawer, and your friend only a red, yellow and blue, a left join from your drawer to your friends would return all the socks from your drawer (red, blue and green) and only matching ones from your friends (red and blue).
Now the IS NULL part says that you want to see all socks from your drawer, where there are not matching pair partners in your friends drawer, so from all your socks the only one with a missing pair is the green sock.
This sentence:
SELECT l.id, l.value
FROM t_left l
LEFT JOIN t_right r
ON r.value = l.value
will have all the elements from t_left table joined to all elements from t_right table. As you probably will now, in an INNER JOIN only rows which matches both tables will be shown, so t_left could have less rows than all the possible rows. In a LEFT JOIN, you have ALL THE ROWS from t_left table plus the matched rows from t_right (if they can be joined). So, what happens with rows in t_left that not matches any t_right row? All the fields from t_right table that would be supposed to contain data will be filled with NULL values. So, if you only select NULL values from right table, you'll find all the values from the t_left table that has no match on the right one, XD
Left join gives all records from left table. If the any key present in left table is missing in right table all right table column will be null, but all the left table values will be present.
It is the basic difference between inner join and left / right join.
Thus when you do 'r.value = null` you get those records from left where there is no matching key in right table.
HTH.
It's Simple Left join funda.....
following Query get all the record from left table and matched record From Right table if it doesn't match than it will return NUll value. at The end you filter data by using where condition which return values that are in Left table but not in right table
SELECT l.id, l.value
FROM t_left l
LEFT JOIN t_right r
ON r.value = l.value
WHERE r.value IS NULL
SELECT * FROM a LEFT JOIN b ON a.v = b.v ... WHERE b.id IS NULL
it's super simple logic:
select from table "a"
join table "b" (if matching rows exists - ON a.v=b.v otherwise use NULL's as values)
WHERE condition: if something is missing (b.id IS NULL) = OK.
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.