Select data from MySQL tables - mysql

I have 2 tables called table_one and table_two, with the following properties:
Both of them have one common column called column_id.
The values of table_one.column_id are unique, whereas the values of table_two.column_id are not.
table_two has two extra columns called ts_one and ts_two. ts_one is not null, but ts_two may be null. Only one row per table_two.column_id permits the ts_two value to be null.
Not all values from table_one.column_id may be presented in table_two.column_id.
For example:
table_one
column_id
1
2
3
4
5
table_two
column_id ts_one ts_two
2 2014-10-01 null
3 2014-10-02 2014-10-03
3 2014-10-05 null
4 2014-10-01 2014-10-05
I need to get all id from table_one.column_id, where:
id in table_one.column_id and not in table_two.column_id(1, 5 satisfy that)
id in table_one.column_id and in table_two.column_id where ts_two is not null and there is no another rows with same id where ts_two with null value - (only 4 satisfy).
Both conditions should be taken into account. The results should include 1, 4, and 5.

You can do this with a single join and an aggregate...
SELECT t1.column_id FROM table_one AS t1
LEFT JOIN (
SELECT column_id, (COUNT(column_id) = COUNT(ts_two)) as no_nulls FROM table_two
GROUP BY column_id
) AS t2
ON t1.column_id = t2.column_id
WHERE t2.column_id IS NULL OR (t2.column_id IS NOT NULL AND t2.no_nulls = TRUE)
SQLFiddle: http://sqlfiddle.com/#!2/14855/8/0
Query everything from table 1
Query each id from table 2, along with whether it has any null ts_two entries for that id.
Joins them together if 1 is missing from 2, or if 1 is in 2 and has no null ts_two entries.

Updated Fiddle: http://sqlfiddle.com/#!2/0b39d2/15/0
SELECT *
FROM Table_one A
LEFT JOIN Table_Two B
on A.Column_ID = B.ColumN_ID
LEFT JOIN (Select column_ID
From table_two
where ts_one is null or ts_two is null) C
ON A.ColumN_Id = C.Column_ID
WHERE C.Column_ID is null
OR B.Column_ID is Null;
What this does:
Returns all data from table 1 in Set 1
Joins to data from table two which are not in 1,5 (Adds data to set 1)
Joins to data from table two that has been filtered to only include records with nulls (creates a 2nd set)
Excludes records where matches exist in 2nd join to table two. (Filters data so that only data in first set and not in second set is returned.

for your first question " id in table_one.column_id and not in table_two.column_id(1, 5)
"
this query should do
SELECT column_id
FROM table_one AS o
WHERE NOT EXISTS (SELECT 1 FROM table_two WHERE column_id = o.column_id)
for your second question "id in table_one.column_id and in table_two.column_id where ts_two is not null and there is no id with a null value - (only 4)"
this query should also do
SELECT t.* FROM table_two AS t
INNER JOIN table_one AS o ON o.column_id = t.column_id
WHERE t.ts_two IS NOT NULL AND NOT EXISTS (SELECT 1 FROM table_two WHERE ts_two IS NULL AND column_id = o.column_id)
the question is so confusing. but from the comments below this should give you what you need
SELECT DISTINCT column_id
FROM (
SELECT column_id
FROM table_one AS o
WHERE NOT EXISTS (SELECT 1 FROM table_two WHERE column_id = o.column_id)
UNION
SELECT t.column_id FROM table_two AS t
INNER JOIN table_one AS o ON o.column_id = t.column_id
WHERE t.ts_two IS NOT NULL AND NOT EXISTS (SELECT 1 FROM table_two WHERE ts_two IS NULL AND column_id = o.column_id)
) AS t

Related

SQL Join where column a in table 1 matches a value from 1 of 4 columns (a-d) in table 2

Goal:
Trying to join together two tables
Table structure:
t1:
name | id
t2:
id_a | id_b | id_c | id_d | favorite color
Problem:
I'm trying to find out the favorite color that corresponds to each name, where the t1.id is found in 1 of the 4 id fields in t2. The tricky part is that the non-matching values aren't null, so a coalesce doesn't work.
What I've tried:
Tried a case when statement in the join, but that seems to be creating some endless loop that is never finishing.
Trying a union, but that is creating some unexpected duplication.
Also tried a multi- on condition (like below), but that's not working:
WITH test AS (
SELECT
t1.*
, t2.*
FROM t1
LEFT JOIN t2
ON ( t1.id = t2.id_a
OR t1.id = t2.id_b
OR t1.id = t2.id_c
OR t1.id = t2.id_d
)
)
SELECT COUNT(*) FROM test
;
Here's an example dataset:
WITH names AS(
SELECT
1 as id , 'alfred' as name
UNION ALL SELECT 2, 'becca'
UNION ALL SELECT 3, 'charlie'
UNION ALL SELECT 4, 'dezi'
)
, color AS(
SELECT
1 as id_a, 6 as id_b, 9 as id_c, 7 as id_d, 'green' as fave_color
UNION ALL SELECT 1,2,6,5, 'orange'
UNION ALL SELECT 5,7,9,3, 'blue'
UNION ALL SELECT 9,4,6,8, 'black'
)
SELECT
n.id
, n.name
, c.fave_color
FROM color c
LEFT JOIN names n
ON n.id IN (c.id_a,c.id_b,c.id_c,c.id_d)
GROUP BY 1,2,3
ORDER BY 1,2
;

MySQL - Select results with specified ID or with null

I have one table:
| ID | ADV_ID | USER_ID |
| 1 | 22 | NULL |
| 2 | 22 | 3 |
| 5 | 44 | NULL |
and now, I want to select row where adv_id = 22 and user_id = 3. If that row doesn't exist, I want to get row where adv_id = 22 and user_id is null.
I tried in that way:
SELECT * FROM `table` WHERE adv_id = 22 AND (user_id = 3 OR user_id is null)
but this query return two rows - with user_id = NULL and with user_id = 3. I want to get one row - with user_id = 3 or (if not exist), with user_id = NULL.
How I can do it in one query?
Thanks.
Use conditional aggregation:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT
ADV_ID,
CASE WHEN COUNT(CASE WHEN USER_ID = 3 THEN 1 END) > 0 THEN 3 END USER_ID
FROM yourTable
) t2
ON t1.ADV_ID = t2.ADV_ID AND
((t1.USER_ID IS NULL AND t2.USER_ID IS NULL) OR (t1.USER_ID = t2.USER_ID))
WHERE
t1.ADV_ID = 22;
Demo
For an explanation, the subquery I have aliased as t2 aggregates over the ADV_ID, and outputs the value 3 if that value occurs in one or more records, otherwise it outputs NULL. Then, we join this subquery back to your original table on the condition that both USER_ID values are NULL, or, if not, that the two USER_ID values match.
You may modify the demo to see that it generates the output you want for other inputs.
SELECT *
FROM test
WHERE ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
UNION ALL
SELECT *
FROM test
WHERE USER_ID IS NULL AND NOT EXISTS (
SELECT 1
FROM test
WHERE ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
)
Select all rows with the first condition: ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
and then UNION ALL with the same table if the first condition is NOT EXISTS.
So we only get results if the first condition is not returned any rows.
The MySQL UNION ALL operator is used to combine the result sets of 2 or more SELECT statements.
try like that:
SELECT * FROM `table` t1 WHERE (t1.adv_id = 44)
AND ((t1.user_id = 3) OR
(NOT EXISTS (select * from `table` t2 where t2.adv_id=t1.adv_id and t2.user_id = 3) AND t1.user_id is null ))
DEMO

Complex SQL Querying: Two queries within the same table?

I have a table like:
ID | LABEL | SOME_VALUE
1 a rand_1
2 a NULL
3 b rand_9
4 c rand_3
5 c rand_3
6 c rand_3
7 d NULL
8 d rand_4
As you can see, ID is unique, label is not unique (can be 1 or more) and some_value is also not unique.
What I want to do is the following:
I want to get a unique list of LABELS, which exist in the database in more than one rows (min 2) and of which rows has SOME_VALUE not NULL.
So I would get:
ID | LABEL | SOME_VALUE
1 a rand_1
2 a NULL
7 d NULL
8 d rand_4
in return.
How can I achieve this?
There are two versions. First one does exactly as listed in results, eliminating rand_3 because even though it appears three times all the values are the same (I don't see distinct condition specified in question).
There must be a better way, but as they say I can't brain today, I have the dumb :-)
select *
from tbl
inner join
(
select label
FROM tbl
GROUP BY Label
HAVING count (distinct some_value)
+ sum(distinct case when some_value is null then 1 else 0 end) > 1
) a
on tbl.label = a.label
Second one retrieves C also following the requirements (some_value being not null for at least one of some_value).
select *
from tbl
inner join
(
select label
FROM tbl
GROUP BY Label
HAVING count(*) > 1 and count(some_value) > 0
) a
on tbl.label = a.label
And there is Sql Fiddle.
The HAVING parameter limits grouped items:
SELECT
Label
FROM dbo.TableName
WHERE NOT Some_Value IS NULL
GROUP BY Label
HAVING COUNT(*) > 2
SELECT t1.*
FROM yourTable t1
JOIN yourTable t2
ON t1.LABEL = t2.LABEL
AND t1.ID < t2.ID
WHERE t1.SOME_VALUE IS NOT NULL
OR t2.SOME_VALUE IS NOT NULL
This should work -
SELECT test.*
FROM (
SELECT label
FROM test
GROUP BY Label
HAVING COUNT(DISTINCT IFNULL(some_value, '~null~')) > 1
) AS tmp
INNER JOIN test
ON tmp.label = test.label;

UPDATE FROM SELECT with foreign key on parent with one query

How to update (change from first select table value second) second_table.first_table_id if first_table.email match in both select.
If it even possible. With one query!
----------------------------------------- UPDATE -----------------------------------------
EXAMPLE:
I need to update foreign key of second table if email field match in first table. I need to compare two query results with different parent_id (parents are in in same table with children)
table_1
-------------------------
| id | parent_id | email |
-------------------------
1 NULL NULL
2 NULL NULL
3 1 joe#m.ru
4 2 bob#f.ly
5 1 bob#f.ly
6 2 kira#.us
table_2
----------------
| id | first_id |
----------------
1 3
2 4
3 5
4 6
I have two parents with ids 1 and 2 and some children (ids: 3,4,5,6).
Also, keep in mind: 1 - old, 2 - new
Task: change foreign key in second table if children email with parent_id = 1 and chilren email with parent_id = 2 match (are the same).
In our example in second table row with id = 3 its foreign key field - first_id has to change from 5 to 4.
Following might get you started
UPDATE Table_2 t2u
SET first_id = (
SELECT t2.first_id
FROM Table_2 t2
INNER JOIN Table_1 t1 ON t1.id = t2.first_id
INNER JOIN (
SELECT parent_id = MAX(parent_id), email
FROM Table_1
GROUP BY
email
) t1p ON t1p.email = t1.email
INNER JOIN Table_1 t1i ON t1i.email = t1p.email
AND t1i.parent_id = t1p.parent_id
WHERE t2u.first_id <> t1i.id)
Test script (SQL Server)
;WITH Table_1 (id, parent_id, email) AS (
SELECT 1, NULL, NULL
UNION ALL SELECT 2, NULL, NULL
UNION ALL SELECT 3, 1, 'joe#m.ru'
UNION ALL SELECT 4, 2, 'bob#f.ly'
UNION ALL SELECT 5, 1, 'bob#f.ly'
UNION ALL SELECT 6, 2, 'kira#.us'
)
, Table_2 (id, first_id) AS (
SELECT 1, 3
UNION ALL SELECT 2, 4
UNION ALL SELECT 3, 5
UNION ALL SELECT 4, 6
)
SELECT t2.*, t1i.id as [update with]
FROM Table_2 t2
INNER JOIN Table_1 t1 ON t1.id = t2.first_id
INNER JOIN (
SELECT parent_id = MAX(parent_id), email
FROM Table_1
GROUP BY
email
) t1p ON t1p.email = t1.email
INNER JOIN Table_1 t1i ON t1i.email = t1p.email
AND t1i.parent_id = t1p.parent_id
WHERE t2.first_id <> t1i.id
Output
id first_id update with
----------- ----------- -----------
3 5 4

UPDATE with INNER JOIN or MIN?

I am trying to transfer some data between tables. The 'NEW' table can have multiple entries of the data that was originally not meant to have multiple entries in the 'OLD' table. I would like to take the data from the 'OLD' table and copy it over to the new table where the NEW.ID is the lowest where new.OtherID=old.OtherID, basically a MIN(ID) per group of OtherID's equal to each other.
'NEW' table
ID | OtherID | Data
1 1 NULL
2 1 NULL
3 2 NULL
4 3 NULL
5 3 NULL
'OLD'
OtherID | Data <br>
1 data1
2 data2
3 data3
4 data4
5 data5
Desired Outcome on updated 'NEW' table:
ID | OtherID | Data <br>
1 1 data1
2 1 NULL
3 2 data2
4 3 data3
5 3 NULL
etc
Thanks!
This is how you could use INNER JOIN with UPDATE in MySQL:
UPDATE NEW n
INNER JOIN (
SELECT
OtherID,
MIN(ID) AS ID
FROM NEW
GROUP BY OtherID
) m ON n.ID = m.ID
INNER JOIN OLD o ON n.OtherID = o.OtherID
SET n.Data = o.Data
You can try:
UPDATE new
SET Data = ( SELECT DATA FROM old WHERE otherID = new.otherID )
WHERE NOT EXIST
( SELECT NULL FROM new AS new2
WHERE new2.id < new.id
AND new2.otherID = new.otherID )
Note that this is standard SQL92 and should work with any RDBMS.
This worked for me in PostgreSQL, though I may have gotten the quoting wrong for MySQL.
UPDATE newtable SET
`Data` = oldtable.`Data`
FROM
oldtable
WHERE
newtable.`ID` IN (
SELECT MIN(sub_newtable.`ID`)
FROM newtable sub_newtable
GROUP BY
sub_newtable.`OtherID`
)
AND newtable.`OtherID` = oldtable.`OtherID`
You can use:
UPDATE `NEW`
LEFT JOIN `OLD`
ON `NEW`.`OtherID` = `OLD`.`ID`
SET `NEW`.`Data` = `OLD`.`Data`
EDIT: I'm sorry, this will update all records that correspond to columns in OLD.