Update when null insert when does not exist - mysql

I have a table with multiple records have the same ID but different values. I want copy records from other table to this table. I want to update if record is null to the minimum position, or insert into the next position if the value does not exist.
Here is my Target table:
ID | Position | Value
1 | 1 | A
2 | 1 | B
2 | 2 | null
2 | 3 | null
2 | 4 | C
3 | 1 | A
4 | 1 | D
4 | 2 | B
Source table:
ID | Value
1 | C
2 | N
3 | B
4 | D
5 | A
6 | null
7 | B
Wanted result table:
ID | Position | Value
1 | 1 | A
1 | 2 | C
2 | 1 | B
2 | 2 | N
2 | 3 | null
2 | 4 | C
3 | 1 | A
3 | 2 | B
4 | 1 | D
4 | 2 | B
5 | 1 | A
7 | 1 | B
My query is:
MERGE Target AS T
USING (SELECT S.ID, MAX(E.POS) AS PosMax, MIN(E.POS) as PosMin, S.Value
FROM Source S
LEFT OUTER JOIN Target E ON S.ID = E.ID
WHERE S.Value IS NOT NULL AND E.Value IS NULL
GROUP BY S.ID, S.Value) AS SC
ON T.ID = SC.ID
WHEN MATCHED AND SC.Value IS NOT NULL AND EG.Value IS NULL AND T.POS = SC.PosMin
THEN
UPDATE SET
EG.Value = SC.Value
WHEN NOT MATCHED AND SC.Value IS NOT NULL
THEN
INSERT (ID, Position, Value)
VALUES (SC.D, ISNULL(SC.PosMax, 0) + 1, SC.Value);
This only updates the null value with the minimum position and insert the value if there is not exist ID. If the ID existed. It will not insert because the Match T.ID = SC.ID.
Example of ID 3, It does not inser value B in position 2.
Does anyone have different approach or strategy? Or I have to write a second query to insert if the ID is the same and value not.
Thanks,
Jay

I think your ON needs the position as well.
ON T.ID = SC.ID and T.pos=SC.minpos.
And the subquery to build SC should be an inner join. You don't want records if you don't have matching values in target.

Related

Select that fill null fields using the id and the values of the same column

I have a table like this:
PK | IDs | name1 | name2
-------------------
1 | 1 | a | null
2 | 1 | a | x
3 | 2 | b | null
4 | 3 | c | z
5 | 2 | null | y
6 | 1 | null | x
7 | 2 | b | null
8 | 2 | null | null
And i want to execute a select in mySQL that give me an output like this:
PK | IDs | name1 | name2
-------------------
1 | 1 | a | x
2 | 1 | a | x
3 | 2 | b | y
4 | 3 | c | z
5 | 2 | b | y
6 | 1 | a | x
7 | 2 | b | y
8 | 2 | b | y
So all the rows with the same id have the same name1 and name2 checking the one that its not null to fill it, if there is no one, it will continue as null.
If you only have one value of name1 or name2 for a given ID value, you can use an aggregation function like MAX (or MIN) which will give you that value from all the value for that IDs in the table. Using a derived table with those values, you can JOIN to the original table to get the name1 and name2 values for each PK, IDs combination:
SELECT d.PK, d.IDs, m.name1, m.name2
FROM data d
JOIN (SELECT IDs, MAX(name1) AS name1, MAX(name2) AS name2
FROM data
GROUP BY IDs) m ON m.IDs = d.IDs
Output:
PK IDs name1 name2
1 1 a x
2 1 a x
3 2 b y
4 3 c z
5 2 b y
6 1 a x
7 2 b y
8 2 b y
Demo on SQLFiddle
You can use correlated sub-query :
select t.pk, t.ids,
coalesce(t.name1, (select t1.name1
from table t1
where t1.pk < t.pk and t1.name1 is not null
order by t1.pk desc
limit 1)
) as name1,
coalesce(t.name2, (select t1.name2
from table t1
where t1.pk < t.pk and t1.name2 is not null
order by t1.pk desc
limit 1)
) as name2
from table t;
You can use update with join:
update t join
(select id, max(name1) as name1, max(name2) as name2
from t
group by id
) tt
on t.id = tt.id
set t.name1 = coalesce(t.name1, tt.name1),
t.name2 = coalesce(t.name2, tt.name2)
where t.name1 is null or t.name2. is null;
Note: This will not change any values that are not NULL, so it is safe even if the values differ for a given id.

Fetch data using mysql left join using where clause on both table

I have 2 tables, custom_leads_fields and custom_leads_fields_option. The primary key of custom_leads_fields is saved in custom_leads_fields_option table as c_id.
I need to fetch all the records from custom_leads_fields table where status = 1 and then I also needs matching records from custom_leads_fields_option where status = 1
custom_leads_fields table
c_id | user_id | label | status |
1 | 591 | A | 1 |
2 | 591 | B | 1 |
3 | 591 | C | 0 |
custom_leads_fields_option table
id | c_id | option | status
1 | 2 | yes | 1
2 | 2 | no | 1
3 | 2 | may | 0
4 | 3 | yy | 1
5 | 3 | zz | 1
Output required:
c_id | label | option
1 | A |
2 | B | yes
2 | B | no
It should return records from first table if status = 1 even if records are not available in second table, but if records are available in second table then only those records should be fetched whose status = 1.
I have written a query but it does not return label 'A' because matching records are not available in other table.
SELECT
`custom_leads_fields`.`c_id` AS `field_id`,
`custom_leads_fields_option`.`id` AS `option_id`,
`custom_leads_fields`.`label`,
`custom_leads_fields_option`.`option`
FROM
`custom_leads_fields`
LEFT JOIN
`custom_leads_fields_option`
ON custom_leads_fields.id = custom_leads_fields_option.custom_leads_field_id
WHERE
(`custom_leads_fields`.`user_id`=591)
AND (`custom_leads_fields`.`status`=1)
AND (`custom_leads_fields_option`.`status`= 1)
I think is left join and the label A doesn't have any relationship with custom_leads_fields_option table. So you can't use custom_leads_fields_option.status = 1. You can try this
select clf.*, clfo.*
from custom_leads_fields clf
left join custom_leads_fields_option clfo
on clf.id = clfo.c_id
where clf.status = 1
and (clfo.status = 1 or clfo.status is null);
sqlfiddle
you need to move status condition to join condition , this will give your desired output. All the records from left table and matched record (with status =1 condition) from right table
SELECT
`custom_leads_fields`.`c_id` AS `field_id`,
`custom_leads_fields_option`.`id` AS `option_id`,
`custom_leads_fields`.`label`,
`custom_leads_fields_option`.`option`
FROM
`custom_leads_fields`
LEFT JOIN
`custom_leads_fields_option`
ON custom_leads_fields.id = custom_leads_fields_option.custom_leads_field_id
AND (`custom_leads_fields_option`.`status`= 1) // changed line
// ^^^^^^^^^^^^^^^^^^
WHERE
(`custom_leads_fields`.`user_id`=591)
AND (`custom_leads_fields`.`status`=1)

MySql/Oracle Remove gaps of numeric column

First of all, I need a solution for Oracle and MySQL.
I Have a folder table :
id | name | parent_id | position
_________________________________
1 | root | null | 1
2 | a | 1 | 1
3 | b | 1 | 2
4 | b1 | 3 | 1
5 | b2 | 3 | 2
6 | c | 1 | 3
7 | d | 1 | 4
8 | e | 1 | 5
given the tree :
root
|_ a
|_ b
| |_b1
| |_b2
|_c
|_d
|_e
The column position has a NOT NULL and UNIQUE constraint.
Problem :
Sometimes i have to delete some folders in a single query (ex : delete folder 'a', 'b1', 'd'). When doing this i have gaps in folders position :
id | name | parent_id | position
_________________________________
1 | root | null | 1
3 | b | 1 | 2
5 | b2 | 3 | 2
6 | c | 1 | 3
8 | e | 1 | 5
So I need to update the table in single request for updating the position column and in a specific order (to prevent the UNIQUE constraint) to obtain the result :
id | name | parent_id | position
_________________________________
1 | root | null | 1
3 | b | 1 | 2
5 | b2 | 3 | 1
6 | c | 1 | 2
8 | e | 1 | 3
Any Idea ?
Thanks
Try this
MERGE
INTO YourTable t1
USING (
SELECT pk_id, gap_ID, row_num() over (order by gap_id) as newGap
FROM YourTable t2
) as sub
ON (t1.pk_id = t2.pk_id)
WHEN MATCHED THEN
UPDATE
SET gap_ID = newGap;
I solved the problem :
Oracle
UPDATE folders t
SET position = ( select count(*)
FROM folders f1 INNER JOIN folders f2 on ( f1.parent_id = f2.parent_id and f1.position >= f2.position )
WHERE f1.id = t.id AND t.parent_id = f1.parent_id
GROUP BY f1.id, f1.position );
MySQL
UPDATE folders f
INNER JOIN ( select f1.id, f1.parent_id, count(*) as newPos
FROM folders f1 INNER JOIN folders f2 on ( f1.parent_id = f2.parent_id and f1.position >= f2.position)
GROUP BY f1.parent_id, f1.position) t on ( t.id = f.id and t.parent_id = f.parent_id)
SET f.position = t.newPos

MySQL copy column value from several columns to one column

I want to copy the latest information from table 1 into table 2.
For the ID i used
insert into Table2(ID) (Select ID FROM Table2). That was not a problem.
CL1 contains the oldest data.
CL3 contains the newest data. So CL2 is between.
Insert into was probably the easiest way to copy the ID from Table1 to Table2
my problem with MySQL is the following.
Table 1
ID | CL1 | CL2 | CL3
A | 1 | 2 | 3
B | 1 | 2 | NULL
C | 1 | 2 | 3
D | 1 | NULL| NULL
E | 1 | 2 | 3
Table 2
ID | CLX
A |
B |
C |
D |
E |
Result should be:
Table 2
ID | CLX
A | 3
B | 2
C | 3
D | 1
E | 3
use GREATEST().
Assuming that CL1 is not nullable, and CL3 cannot have value unless CL2 is filled up.
INSERT INTO table2(ID, CLX)
SELECT ID, GREATEST(CL1, COALESCE(CL2, CL1), COALESCE(CL3, CL1))
FROM table1
SQLFiddle Demo
Thank you,
Coalesce was the function I need for my queries.
`Select ID, COALESCE(CL3,CL2,CL1) as latest from table1`

Mysql: return 0 when result 1 row in a table, other table result is 2 rows

How to display result
Temp | Order | Payment
A | 5 | 3
A | 4 | 0
B | 2 | 2
B | 0 | 3
C | 3 | 0
with: first_table:
Name | Description
A | Description A
B | Description B
C | Description C
second_table:
Name | order
A | 5
A | 4
B | 2
C | 3
third_table:
Name | Payment
A | 3
B | 2
B | 3
Meaning:
there are 2 order (value: 4,5) with Name=A.
there is 1 order (value: 3) with Name = A
And result I want to display:
Name | Order | Payment
A | 4 | 3 |
A | 5 | 0 |
Any help me?
You need to use JOIN.
From your example there is no Payment for order A5 so you should use LEFT JOIN which will result in Payment being NULL for that row (A5). LEFT JOIN basically means "select rows from first table and if there is no match in second table, put NULL".
Simply do a LEFT JOIN on second_table and third_table on field Name where Name=a. It's a very basic example so I won't give you the exact query, you should learn it by yourself.