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
Related
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.
I have a table veicoli (vehicles) like this:
-------------------------------
| ID | Modello | Targa |
-------------------------------
| 1 | IVECO | XA123WE |
-------------------------------
| 2 | IVECO | CF556XD |
-------------------------------
| 3 | FIAT | AS332ZZ |
-------------------------------
| 4 | GOLF | GF567YU |
-------------------------------
For each vehicle I have none, one or multiple revisioni_veicolo (revisions) (the one with bigger DateExpiring is the one I need to check if revision is still valid or not based on today date)
-------------------------------------------------------------------
| ID | veicoli_ID | DateExpiring | Pass_Success |
-------------------------------------------------------------------
| 1 | 1 | 2019-07-01 | 1
------------------------------------------------------------------
| 2 | 1 | 2020-10-01 | 0
-------------------------------------------------------------------
| 3 | 2 | 2019-11-25 | 1
-------------------------------------------------------------------
| 4 | 2 | 2018-10-20 | 1
-------------------------------------------------------------------
| 5 | 4 | 2017-10-20 | 1
-------------------------------------------------------------------
Based on my example above (today is 2019-10-29):
Vehicle: ID = 1 has a revision still active (2020-10-01) but not passed (Pass_success = 0)
Vehicle: ID = 2 has a revision still active (2019-11-25) and passed (Pass_success = 1)
Vehicle: ID = 3 has no revision yet
Vehicle: ID = 4 has revision, but no active revision (last expired on 2017-10-20) but the last one passed the check (Pass_success = 1)
What I need is to have 3 new custom columns created dynamically on my query result:
-------------------------------------------------------------------------------------------
| ID | Modello | Targa | RevisionPresent | RevisionStillActive | LastRevisionPassed |
-------------------------------------------------------------------------------------------
| 1 | IVECO | XA123WE | true | true | false
-------------------------------------------------------------------------------------------
| 2 | IVECO | CF556XD | true | true | true
-------------------------------------------------------------------------------------------
| 3 | FIAT | AS332ZZ | false | false | false
-------------------------------------------------------------------------------------------
| 4 | GOLF | GF567YU | true | false | true
-------------------------------------------------------------------------------------------
I tried to start with my old post: MYSQL INNER JOIN to get 3 types of result
But I'm very confused using nested JOIN
I tried starting a fiddle but i'm stuck on syntax error: http://sqlfiddle.com/#!9/3c70bf/2
You need a LEFT JOIN of the tables and conditional aggregation:
select v.ID, v.Modello, v.Targa,
max(r.DataScadenzaRevisione is not null) RevisionPresent,
coalesce(max(r.DataScadenzaRevisione >= current_date()), 0) RevisionStillActive,
max(case when r.DataScadenzaRevisione = g.maxdate then r.EsitoPositivo else 0 end) LastRevisionPassed
from veicoli v
left join revisioni_veicolo r on r.veicoli_ID = v.id
left join (
select veicoli_id, max(DataScadenzaRevisione) maxdate
from revisioni_veicolo
group by veicoli_id
) g on g.veicoli_ID = v.id
group by v.ID, v.Modello, v.Targa
See the demo.
Results:
| ID | Modello | Targa | RevisionPresent | RevisionStillActive | LastRevisionPassed |
| --- | ------- | ------- | --------------- | ------------------- | ------------------ |
| 1 | IVECO | XA123WE | 1 | 1 | 0 |
| 2 | IVECO | CF556XD | 1 | 1 | 1 |
| 3 | FIAT | AS332ZZ | 0 | 0 | 0 |
| 4 | GOLF | GF567YU | 1 | 0 | 1 |
...
LEFT JOIN (SELECT a.veicoli_ID, a.EsitoPositivo AS StatoUltimaRevisione,
a.DataScadenzaRevisione FROM revisioni_veicolo) a
...
There's two things wrong with this.
The alias a is defined for this subquery, so you can't reference it inside the subquery. But you don't need to qualify the columns in this subquery anyway - you didn't do this in other subqueries, so I'm not sure why you did it in this case.
You don't have any join condition for this join. MySQL is a little bit inconsistent about when join conditions are required. But in this case, you need one.
After I tested the query with these two corrections, it works.
Basically you just need to look at the last revision of each vehicule to produce that resultset.
You can do the filtering with a correlated subquery:
select
v.ID,
v.Modello,
v.Targa,
(DataScadenzaRevisione >= now()) RevisionPresent,
(DataScadenzaRevisione >= now() and EsitoPositivo = 1) RevisionStillActive,
(EsitoPositivo = 1) LastRevisionPassed
from
veicoli v
left join revisioni_veicolo r
on r.veicoli_ID = v.ID
and r.DataScadenzaRevisione = (
select max(DataScadenzaRevisione)
from revisioni_veicolo r1
where r1.veicoli_ID = v.ID
)
You can check the results with your sample data in this db fiddle.
Or you can use a window function (this requires MySQL 8.0):
select
v.ID,
v.Modello,
v.Targa,
(DataScadenzaRevisione >= now()) RevisionPresent,
(DataScadenzaRevisione >= now() and EsitoPositivo = 1) RevisionStillActive,
(EsitoPositivo = 1) LastRevisionPassed
from (
select
v.*,
r.*,
row_number() over(partition by ID order by r.DataScadenzaRevisione desc) rn
from veicoli v
left join revisioni_veicolo r on r.veicoli_ID = v.ID
) where coaelesce(rn, 1) = 1
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.
I have a two tables.
work:
+----+----------+
| id | position |
+----+----------+
| 1 | 1 |
| 2 | 2 |
+----+----------+
content:
+----+---------+------+-------------+
| id | work_id | name | translation |
+----+---------+------+-------------+
| 1 | 1 | Kot | 1 |
| 2 | 1 | Cat | 2 |
| 3 | 2 | Ptak | 1 |
| 4 | 2 | Bird | 2 |
| 5 | 2 | Ssss | 3 |
+----+---------+------+-------------+
I want to get result like this:
+----+------+----------+
| id | name | sortName |
+----+------+----------+
| 1 | Kot | NULL |
| 1 | Cat | NULL |
| 2 | Ptak | Ssss |
| 2 | Bird | Ssss |
+----+------+----------+
My not working query is here:
select
w.id,
c.name,
cSort.name as sortName
from
work w
LEFT JOIN
content c
ON
(w.id=c.work_id)
LEFT JOIN
content cSort
ON
(w.id=cSort.work_id)
WHERE
c.translation IN(1,2) AND
cSort.translation=3
ORDER BY
sortName
I want to get for each work at least one translation and secound if exist (translation=1 always exist). And for every row I want special column with translation used to sort. But Not always this translation exist for work.id. In this example I want to sort work by translation=3.
Sorry for my not fluent english. Any ideas?
Best regards
/*
create table work ( id int, position int);
insert into work values
( 1 , 1 ),
( 2 , 2 );
create table content(id int, work_id int, name varchar(4), translation int);
insert into content values
( 1 , 1 , 'Kot' , 1),
( 2 , 1 , 'Cat' , 2),
( 3 , 2 , 'Ptak' , 1),
( 4 , 2 , 'Bird' , 2),
( 5 , 2 , 'Ssss' , 3);
*/
select w.id,c.name,(select c.name from content c where c.work_id = w.id and c.translation = 3) sortname
from work w
join content c on w.id = c.work_id
where c.translation <> 3;
result
+------+------+----------+
| id | name | sortname |
+------+------+----------+
| 1 | Kot | NULL |
| 1 | Cat | NULL |
| 2 | Ptak | Ssss |
| 2 | Bird | Ssss |
+------+------+----------+
So translation is also a work_id and you consider translation = 3 a translation in your example and translation <> 3 an original. You want to join each original record with every translation record where the latter's work_id matches the former's translation.
I think you are simply confusing IDs here. It should be ON (w.translation = cSort.work_id).
Another way to write the query:
select o.work_id as id, o.name, t.name as sortname
from (select * from content where translation <> 3) o
left join (select * from content where translation = 3) t
on t.work_id = o.translation
order by t.name;
There seems to be no need to join table work.
I'd like to add that the table design is a bit confusing. Somehow it is not clear from it what is a translation for what. In your example you interpret translation 3 as a translation for the non-three records, but this is just an example as you say. I don't find this readable.
UPDATE: In order to sort your results by work.position, you can join that table or use a subquery instead. Here is the order by clause for the latter:
order by (select position from work w where w.id = o.work_id);
I have 4 tables:
secu_content
| id | created | modified |
| 910 | 26/12/1982 | 28/12/1984 |
| 911 | 24/12/1982 | 25/12/1984 |
secu_data
| element_id | field_id | data |
| 1 | 1 | 25/12/1984 |
| 2 | 1 | 26/12/1984 |
| 3 | 1 | 27/12/1984 |
| 4 | 1 | 25/12/1984 |
| 4 | 2 | google.com |
secu_elements
| id | item_id |
| 1 | 891 |
| 2 | 711 |
| 3 | 204 |
| 4 | 911 |
secu_fields
| id | type |
| 1 | date |
| 2 | input |
Table secu_content, contains many articles, where the id is the article id.
The other 3 tables gives additional information and I want to join them.
I want to get results that includes all secu_content rows and all the columns + calc_date + calc_link
calc_date <- the data column from secu_data where field_id=1 (see secu_fields)
calc_link <- the data column from secu_data where field_id=2 (see secu_fields)
The problem is that I get 2 rows where secu_content id=911 (one row with the correct calc_date and second row with the correct calc_link), and I need one row with both.
This is my SQL:
SELECT a.id
, a.created
, a.modified
, fe.item_id AS calc_date_item_id
, fd.data AS calc_date
, CASE WHEN fd.data IS NOT NULL AND ff.type = "date" THEN fd.data
WHEN a.modified = '0000-00-00 00:00:00' THEN a.created ELSE a.modified
END as calc_date
, CASE WHEN fd.data IS NOT NULL AND ff.type = "input" THEN fd.data
END as calc_link
FROM secu_content AS a
LEFT
JOIN secu_fieldsandfilters_elements AS fe
ON fe.item_id = a.id
AND fe.content_type_id=1
LEFT
JOIN secu_fieldsandfilters_data AS fd
ON fd.element_id = fe.id
LEFT
JOIN secu_fieldsandfilters_fields as ff
ON ff.id = fd.field_id
ORDER BY a.id DESC;
Thanks in advance
Israel
Fast and dirty solution is to use second join to secu_data like that (simplified, add logic you need)
SELECT id, d1.data as `calc_date`, d2.data as `calc_link`
FROM secu_content
LEFT JOIN secu_data d1 ON secu_content.id = d1.element_id AND field_id = 1
LEFT JOIN secu_data d2 ON secu_content.id = d2.element_id AND field_id = 2