Select IDs from table where col_2 is not null (duplicate id) - mysql

I have a table that contains the following data
ID | Col_2
A | 'ABC'
A | 'GHI'
A | null
B | 'null'
B | 'HJH'
B | 'NBN'
C | null
I have two cases to cater :
Duplicate Ids:
Incase of duplicate ids I only want those IDs which do not have null in col_2
E.g.
Query should return :
A | 'ABC'
A | 'GHI'
B | 'HJH'
B | 'NBN'
Non Duplicate Id:
Incase of non duplicate id the query should return result irrespective of the value present in col_2
So the final result of the query should be
ID | Col_2
A | 'ABC'
A | 'GHI'
B | 'HJH'
B | 'NBN'
C | null
I have managed to create the following query where it is fulfilling the duplicate id case not the non duplicate case.
Query :
select id,col_2
from mytable
group by id,col_2
having (sum(case when col_2 is not null then 1 else 0 end) > 0)
What changes should be made in the query to cater the non duplicate case also.
Thanks in advance!!!

Assuming NULL is NULL and not a string and that you have only one NULL value per id, you can do something like this:
select t.*
from t
where t.col_2 is not null or
not exists (select 1 from t t2 where t2.id = t.id and t2.col_2 is not null);
If your null values can be duplicated and you want only one row for them, then tweak this to:
select t.*
from t
where t.col_2 is not null
union all
select distinct t.*
from t
where not exists (select 1 from t t2 where t2.id = t.id and t2.col_2 is not null);
Here is a db<>fiddle.
For performance, you want an index on (id, col_2).
If you just want the col_2 values for each id, you can concatenate them on each row:
select id, group_concat(col_2)
from t
group by id;
Another alternative uses window functions:
select t.id, col_2
from (select t.*,
rank() over (partition by id order by col_2 is not null desc) as seqnum
from t
) t
where seqnum = 1;

Related

MYSQL: Merge datas with same id and put all the datas to another new column

Good day,
I have a problem regarding how to build my query in MYSQL.
I have this raw data.
ID | NAME | VALUE | DESCRIPTION
ID-0001 | ARV1 | 10200 | First description
ID-0002 | ARV2 | 10300 | Second description
ID-0001 | ARV1 | 10400 | Added Description
And I want to achieve an output like this:
Where I want to merge the datas with the same ID and put the other tables in another column and with new column name.
ID | NAME | VALUE 1 | DESCRIPTION 1 | VALUE 2 | DESCRIPTION 2 |
ID-0001 | ARV1 | 10200 | First description | 10400 | Added Description |
ID-0002 | ARV2 | 10300 | Second description | | |
I badly need help. Thanks!
ID wise row is serialized as per storing data in DB that's why ORDER BY is not use. This query is alternative of PIVOT. But if more row exists for a same ID then PIVOT is better option. If any primary key is existed in table then use that PK at ORDER BY clause after PRTITION BY.
-- MySQL (v5.8)
SELECT t.id
, MAX(t.name) name
, MAX(CASE WHEN t.row_num = 1 THEN value END) value1
, MAX(CASE WHEN t.row_num = 1 THEN description END) description1
, MAX(CASE WHEN t.row_num = 2 THEN value END) value2
, MAX(CASE WHEN t.row_num = 2 THEN description END) description2
FROM (SELECT *
, ROW_NUMBER() OVER (PARTITION BY id) row_num
FROM test) t
GROUP BY t.id;
Please check from url https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=e4a5999a3536b9e042d7ed92fc392de7
Suppose table have a primary key column name p_id. Then ROW_NUMBER() will
ROW_NUMBER() OVER (PARTITION BY id ORDER BY p_id) row_num
N.B: By default p_id sorts the data in ascending order but if need descending order then use DESC.
WITH
cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY ID, NAME ORDER BY rowid) rn
FROM test
)
SELECT t1.ID, t1.NAME,
t1.VALUE value1, t1.DESCRIPTION description1,
t2.VALUE value2, t2.DESCRIPTION description2
FROM cte t1
LEFT JOIN cte t2 ON t1.ID = t2.ID
AND t1.NAME = t2.NAME
AND t2.rn = 2
WHERE t1.rn = 1;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=e10ac4664ef614300c23a987f83e805d
PS. If a column which defines the rows priority not exists (rowid in the fiddle - it can be, for example, the creation datetime or something else) then the data order of the values pairs is not determined.

Insert if last row's field meets clause or sub query result is equal to 0

I am trying to get a single insert that applies the following
______________________________
| id | item (fk) | condition |
| 1 | 3 | NEW |
| 2 | 3 | USED |
| 3 | 2 | USED |
______________________________
What I am trying to achieve is the following:
Given an item id = 3 for example, I would like to insert a new rows in this table if one of these two conditions are met:
the row with the largest id is not "NEW"`
the count of entries of this item (id = 3) is equal to 0
The closest I got so far was:
INSERT INTO tableA (item, condition)
SELECT (itemId, "NEW") FROM (
SELECT condition, count(*) as rowCount
FROM tableA
WHERE item = itemId
ORDER BY id DESC LIMIT 1 ) a
WHERE a.condition <> "NEW" OR a.rowCount = 0;
But the count(*) messes up with the ordering.. Any clue?
I am thinking about this as an aggregation subquery with no GROUP BY. This always returns exactly one row, so you will still have a row even if cnt is 0:
INSERT INTO tableA (item, condition)
SELECT a.itemId, a.condition
FROM (SELECT #itemId as itemId, 'NEW' as condition,
COUNT(*) as cnt,
MAX(id) as max_id,
MAX(CASE WHEN condition = 'NEW' THEN id END) as max_new
FROM tableA
WHERE item = #itemId
) a
WHERE cnt = 0 OR max_new is null or max_new < max_id;
You need a subquery that gets the row with the largest ID for that item:
SELECT condition
FROM tableA
WHERE item = 3
ORDER BY id DESC
LIMIT 1
If there are no rows for this item, this subquery will return NULL. You can use the null-safe equality operator to handle this.
INSERT INTO tableA (item, condition)
SELECT 3, "NEW"
FROM DUAL
WHERE NOT (
SELECT condition
FROM tableA
WHERE item = 3
ORDER BY id DESC
LIMIT 1
) <=> "NEW"

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

MYSQL Updating row to maximum value of similar rows

I have a table like this in MYSQL:
ID | NAME | VALUE |
----------------------------
1 | Bob | 1 |
2 | Bob | 2 |
3 | Jack | 5 |
4 | Jack | 8 |
5 | Jack | 10 |
and I'm trying to update the VALUE column to the highest value of rows with same NAME. So the result should be:
ID | NAME | VALUE |
----------------------------
1 | Bob | 2 |
2 | Bob | 2 |
3 | Jack | 10 |
4 | Jack | 10 |
5 | Jack | 10 |
I managed to get the max value like this:
SELECT MAX(Value) max FROM `table` GROUP BY Name having count(*) >1 AND MAX(Value) != MIN(Value)
But can't figure out how to put it in my update
Update table set Value = (SELECT MAX(Value) max FROM `table` GROUP BY Name having count(*) >1 AND MAX(Value) != MIN(Value))
Doesn't work. I'd appreciate any help.
This is easier than other answers are making it.
UPDATE MyTable AS t1 INNER JOIN MyTable AS t2 USING (Name)
SET Value = GREATEST(t1.Value, t2.Value);
You don't have to find the largest value. You just have to join each row to the set of rows with the same name, and set the Value to the greater Value of the two joined rows. This is a no-op on some rows, but it will apply to every row in turn.
http://sqlfiddle.com/#!9/f79a3/1
UPDATE t1
INNER JOIN (SELECT name, MAX(`value`) max_value
FROM t1 GROUP BY name) t2
ON t1.name = t2.name
SET t1.value = t2.max_value;
Create a temporary table consisting of ID NAME and MAX VALUE as follows:
CREATE TEMP TABLE TABLE1 AS
(SELECT NAME,MAX(Value) value FROM `table` GROUP BY Name having count(*) >1
AND MAX(Value) != MIN(Value)
);
Use this temporary table to do your update as follows:
UPDATE
Table_A
SET
Table_A.value = Table_B.value
FROM
`table` AS Table_A
INNER JOIN TABLE1 AS Table_B
ON Table_A.NAME = Table_B.NAME
Also this code is somewhat of an approximation as i am not familiar with mysql but i am familiar with sql.
Let me know if this doesn't help.
Simple left join would do the trick.
Try this out and let me know in case of any queries.
select a.id,a.name,b.value
from
table a
left join
(select name,max(value) as value from table group by name) b
on a.name=b.name;
You may use this query. The table is joined with a subquery (table t2) that contains the results you want to update your table with:
UPDATE `table` t1,
(SELECT Name, MAX(Value) maxv, MIN(Value) minv
FROM `table`
GROUP BY Name
HAVING COUNT(*)>1 AND maxv != minv) t2
SET t1.Value = t2.maxv
WHERE t1.Name = t2.Name;
If you want to know how will the values be updated, you can first run an equivalent SELECT query:
SELECT t1.*, t2.maxv
FROM `table` t1,
(SELECT Name, MAX(Value) maxv, MIN(Value) minv
FROM `table`
GROUP BY Name
HAVING COUNT(*)>1 AND maxv != minv) t2
WHERE t1.Name = t2.Name;
This query will display all the fields of table, followed by the new value maxv. You can check the current value and the new value, and if it looks fine, you may run the UPDATE query.

MySQL find duplicates in multiple columns

I have a table with user IDs split into 2 columns. (To explain this a little more, we capture the IDs of participants by scanning barcodes. Sometimes the barcode scanner function doesn't work for whatever reason, so we also allow manual entry of the ID, IF the barcode scanner doesn't work.) This results in data like the following:
+------+-----------+
| ID | ID_MANUAL |
+------+-----------+
| A | NULL |
| NULL | A |
| B | NULL |
| B | NULL |
| NULL | C |
| C | NULL |
| NULL | D |
| NULL | D |
+------+-----------+
I want to find all of the duplicate IDs, taking both columns into account. It's easy to find the duplicates that are only in 1 column ("B" and "D"). But how do I find the duplicates "A" and "C"? Ideally, the query would find and return ALL duplicates (A,B,C, and D).
Thanks!
Try this:
SELECT DUP.* FROM (SELECT ID FROM yourtable) ORI
LEFT JOIN yourtable DUP ON DUP.ID = ORI.ID_MANUAL WHERE DUP.ID IS NOT NULL
An advice: a field named ID m,ust be unique and not null. But if you have this structure, you can try this:
SELECT id
FROM yourtable t
WHERE id is not null
AND
(SELECT COUNT(*)
FROM yourtable t2
WHERE t2.id = t.id) +
(SELECT COUNT(*)
FROM yourtable t3
WHERE t3.id_manual = t.id) > 1
UNION
SELECT id_manual
FROM yourtable t
WHERE id_manual is not null
AND
(SELECT COUNT(*)
FROM yourtable t2
WHERE t2.id = t.id_manual) +
(SELECT COUNT(*)
FROM yourtable t3
WHERE t3.id_manual = t.id_manual) > 1
You can go on Sql Fiddle
You could try UNION ALL here:
select id,count(*)
from
(
select id
from yourtable
union all
select id_manual as id
from yourtable
) a
group by id
having count(*) >1;
try:
select id, count(*)
from
(
select id
from data
where id_manual is null
union all
select id_manual as id
from data
where id is null
) a
group by id
having count(*) > 1;
and
select id, id_manual
from data
group by id, id_manual
having count(*) > 1;
You can do this with a simple JOIN, using COALESCE and DISTINCT if you have a surrogate auto-increment primary key:
SELECT DISTINCT s2.pk, s2.ID, s2.ID_MANUAL
FROM scans s1
JOIN scans s2
ON COALESCE(s2.ID, s2.ID_MANUAL) = COALESCE(s1.ID, s1.ID_MANUAL)
AND s2.pk > s1.pk
This will exclude the original record, so you could delete the records returned in this result set.
Here's the SQL Fiddle.