SQL - complex SELECT query to UPDATE - mysql

SQL noob here.
So i have a table with a lot of products. Some of these products are clothing, meaning i have e.g. six different SKU's for one product due to different sizes. I now want to set the first product variant as the parent for each group. Product IDs and therefore the parent IDs are UUIDs. I managed to write a SELECT query which is taking all the product numbers, finds all groups (by cutting off the last pair of numbers) and assigns all the respective parent (uu)IDs and parent product numbers (for human readable comparison) - it works absolutely fine.
But i have no clue on how to convert this rather complex SELECT into an UPDATE. Anyone having ideas? Version is MySQL 8
Table1 looks like this (with all unused columns cut out):
ID |product_number |parent_id
-------------------------------------------
[UUID]1-1 |123-456-01 | NULL
[UUID]1-2 |123-456-02 | NULL
[UUID]1-3 |123-456-03 | NULL
[UUID]1-4 |123-456-04 | NULL
[UUID]2-1 |987-65-43-21-01 | NULL
[UUID]2-2 |987-65-43-21-02 | NULL
[UUID]2-3 |987-65-43-21-03 | NULL
[UUID]2-4 |987-65-43-21-04 | NULL
My SELECT query:
SELECT ArticleNumber, ArticleGroup, ParentID, t3.id as ID
FROM (
SELECT t2.product_number as ArticleNumber, GroupTable.GroupNr as ArticleGroup, GroupTable.product_number as ParentID
FROM (
SELECT MIN(result.product_number) as product_number, result.GroupNr
FROM (
SELECT product_number,
SUBSTRING_INDEX(product_number, "-", (LENGTH(product_number) - LENGTH(REPLACE(product_number, "-", "")))) as GroupNr
FROM table1.product
) result
WHERE LENGTH(result.GroupNr) > 0
GROUP BY result.GroupNr
ORDER BY GroupNr
) as GroupTable
JOIN table1.product as t2
ON t2.product_number like concat(GroupTable.GroupNr, '%') AND t2.product_number != GroupTable.product_number
ORDER BY GroupTable.GroupNr
) as Energija
JOIN table1.product as t3
ON t3.product_number = Energija.ParentID
I want to update the parent_id so that Table1 looks like this:
ID |product_number |parent_id
-------------------------------------------
[UUID]1-1 |123-456-01 | NULL
[UUID]1-2 |123-456-02 | [UUID]1-2
[UUID]1-3 |123-456-03 | [UUID]1-2
[UUID]1-4 |123-456-04 | [UUID]1-2
[UUID]2-1 |987-65-43-21-01 | NULL
[UUID]2-2 |987-65-43-21-02 | [UUID]2-2
[UUID]2-3 |987-65-43-21-03 | [UUID]2-2
[UUID]2-4 |987-65-43-21-04 | [UUID]2-2
It works in the SELECT query, i just don't know how to make an UPDATE out of this.
Sample table with UUIDs switched for string:
CREATE TABLE table1.product (
id varchar(255),
product_number varchar(255),
parent_id varchar(255));
INSERT INTO Table1.product (
id, product_number, parent_id)
VALUES(
'1-1',
'123-456-01',
NULL),
(
'1-2',
'123-456-02',
NULL),
(
'1-3',
'123-456-03',
NULL),
(
'1-4',
'123-456-04',
NULL),
(
'2-1',
'987-65-43-21-01',
NULL),
(
'2-2',
'987-65-43-21-02',
NULL),
(
'2-3',
'987-65-43-21-03',
NULL),
(
'2-4',
'987-65-43-21-04',
NULL);

You just need to slightly adapt your query and set the parentUuid in the update statement, where the product uuid matches.
In the example code below I adapted your query to get a mapping between the products uuid and the parent uuid. Then I update the table setting the parent_id from the product-table where the products uuid matches the product uuid from the query.
UPDATE table1.product p
SET parent_id = (
SELECT parentUUID
FROM (SELECT t3.id as parentUUID, Energija.productuuid as productUuid
FROM (
SELECT t2.id as productuuid,
t2.product_number as ArticleNumber,
GroupTable.GroupNr as ArticleGroup,
GroupTable.product_number as ParentID
FROM (
SELECT MIN(result.product_number) as product_number, result.GroupNr
FROM (
SELECT product_number,
SUBSTRING_INDEX(product_number, "-",
(LENGTH(product_number) - LENGTH(REPLACE(product_number, "-", "")))) as GroupNr
FROM table1.product
) result
WHERE LENGTH(result.GroupNr) > 0
GROUP BY result.GroupNr
ORDER BY GroupNr
) as GroupTable
JOIN table1.product as t2
ON t2.product_number like concat(GroupTable.GroupNr, '%') AND
t2.product_number != GroupTable.product_number
ORDER BY GroupTable.GroupNr
) as Energija
JOIN table1.product as t3
ON t3.product_number = Energija.ParentID) parentMapping
where parentMapping.productuuid = p.id);

Related

MySQL: Returning rows which are matching the conditions: 1. Dates are ascending and 2. Views are descending

I have a table (tab1) which contains id, trending_date and views coulmns. I have to write a query to return those records whose views are decreasing as the trending_dates are going by. E.g. -
|ID |Trending_Date | Views |
|---|--------------|-------|
|A |2021-01-03 | 10 |
|B |2020-10-30 | 8 |
|A |2021-02-05 | 9 |
|B |2020-11-02 | 11 |
My intended query will return only the records having "A" as ID. Need help on developing this query...
Thanks,
Amitava
If you want only the IDs in the results then aggregate and use GROUP_CONCAT() in the HAVING clause:
SELECT ID
FROM tablename
GROUP BY id
HAVING COUNT(*) = COUNT(DISTINCT Views)
AND GROUP_CONCAT(Views ORDER BY Views DESC) = GROUP_CONCAT(Views ORDER BY Trending_Date)
GROUP_CONCAT(Views ORDER BY Views DESC) sorts the Views of each ID descending and GROUP_CONCAT(Views ORDER BY Trending_Date) sorts the the Views of each ID by Trending_Date ascending.
If the 2 results are the same this means that Views are monotonically decreasing.
The condition COUNT(*) = COUNT(DISTINCT Views) filters out IDs with duplicate Views.
If you want full rows of the table use the above query with the operator IN:
SELECT *
FROM tablename
WHERE id IN (
SELECT ID
FROM tablename
GROUP BY id
HAVING COUNT(*) = COUNT(DISTINCT Views)
AND GROUP_CONCAT(Views ORDER BY Views DESC) = GROUP_CONCAT(Views ORDER BY Trending_Date)
)
See the demo.
With MySQL 8 you can use the window function LAG
I added also a version for MySQL 5.x
CREATE TABLE tab1 (
`ID` VARCHAR(1),
`Trending_Date` VARCHAR(10),
`Views` INTEGER
);
INSERT INTO tab1
(`ID`, `Trending_Date`, `Views`)
VALUES
('A', '2021-01-03', '10'),
('B', '2020-10-30', '8'),
('A', '2021-02-05', '9'),
('B', '2020-11-02', '11');
SELECT ID,`Trending_Date`,`Views`
FROM
(SELECT ID,`Trending_Date`,
`Views` ,
LAG(`Views`, 1) OVER (
PARTITION BY `ID`
ORDER BY `Trending_Date`
) lastview
FROM tab1) t1
WHERE `Views` < lastview
ID | Trending_Date | Views
:- | :------------ | ----:
A | 2021-02-05 | 9
SELECT ID,`Trending_Date`,`Views`
FROM
(SELECT
`Trending_Date`,
IF (#id <> ID,#views := 0,#views := #views) idchange,
IF (#views > `Views`,1,0) smaller,
#views := `Views` as Views,
#id := ID as ID
FROM tab1,(SELECT #views := 0,#id := '') t1
ORDER BY ID,`Trending_Date`) t2
WHERE smaller = 1;
ID | Trending_Date | Views
:- | :------------ | ----:
A | 2021-02-05 | 9
db<>fiddle here
I have added two queries. First one with window function for MySQL 8.0 and second one is using subquery for older version of MySQL.
CREATE TABLE tab1 (
`ID` VARCHAR(1),
`Trending_Date` VARCHAR(10),
`Views` INTEGER
);
INSERT INTO tab1
(`ID`, `Trending_Date`, `Views`)
VALUES
('A', '2021-01-03', '10'),
('B', '2020-10-30', '8'),
('A', '2021-02-05', '9'),
('B', '2020-11-02', '11');
Query#1 (For MySQL 8.0)
select id
from
(
select id,trending_date,views,max(trending_date)over(partition by id)maxtdate,
min(trending_date)over(partition by id)mintdate
from tab1
)t
group by id
having max(case when trending_date=maxtdate then views end) < max(case when trending_date=mintdate then views end)
Output:
id
A
Query#2 (For older version of MySQL)
select id
from
(
select id,trending_date,views,(select max(trending_date)from tab1 t2 where t1.id=t2.id)maxtdate,
(select min(trending_date)from tab1 t2 where t1.id=t2.id)mintdate
from tab1 t1
)t
group by id
having max(case when trending_date=maxtdate then views end) < max(case when trending_date=mintdate then views end)
Output:
id
A
db<fiddle here

MySQL - SELECT query - oldest and youngest date

I have to build one SQL SELECT query. Table structure is as follows:
[ID] [PHONE] [description] [DATE]
[1] [600898367] [main] [2016-01-23]
[2] [600898367] [] [2016-01-24]
[3] [600898367] [] [2016-01-26]
[4] [600898367] [] [2016-01-28]
[5] [662349093] [main] [2016-01-10]
[6] [662349093] [] [2016-01-21]
[7] [662349093] [] [2016-01-30]
[8] [662349093] [] [2016-01-31]
You have here different records grouped within the same telephone number. The first (the oldest) occurance is marked with [main] flag. There's no two identical numbers with [main] flag.
I want to select each [main] record and additionaly one youngest with the same phone number, so the result should give records 1,4,5,8.
Please help.
Use a WHERE clause to give you the records with the main flag. Use MAX to get the most recent record and JOIN to get the additional columns. Finally, do a UNION ALL to combine the result.
-- Get the main records first
SELECT *
FROM tbl
WHERE description = 'main'
UNION ALL
-- Get the most recent records
SELECT b.*
FROM (
SELECT
t.PHONE,
MAX(DATE) AS MaxDate
FROM tbl t
GROUP BY PHONE
) a
INNER JOIN tbl b -- Do a JOIN to get the additional columns
ON b.PHONE = a.PHONE
AND b.DATE = a.MaxDate
Try this;)
SQL Fiddle
MySQL 5.6 Schema:
CREATE TABLE table1
(`ID` int, `PHONE` int, `description` varchar(4), `DATE` varchar(11))
;
INSERT INTO table1
(`ID`, `PHONE`, `description`, `DATE`)
VALUES
(1, 600898367, 'main', '2016-01-23'),
(2, 600898367, NULL, '2016-01-24'),
(3, 600898367, NULL, '2016-01-26'),
(4, 600898367, NULL, '2016-01-28'),
(5, 662349093, 'main', '2016-01-10'),
(6, 662349093, NULL, '2016-01-21'),
(7, 662349093, NULL, '2016-01-30'),
(8, 662349093, NULL, '2016-01-31')
;
Query 1:
select t.*
from table1 t
inner join (
select `PHONE`, max(`DATE`) as `DATE` from table1 group by `PHONE`
) t1 on t.`PHONE` = t1.`PHONE` and (t.`DATE` = t1.`DATE` or t.`description` = 'main')
order by t.`ID`
Results:
| ID | PHONE | description | DATE |
|----|-----------|-------------|------------|
| 1 | 600898367 | main | 2016-01-23 |
| 4 | 600898367 | (null) | 2016-01-28 |
| 5 | 662349093 | main | 2016-01-10 |
| 8 | 662349093 | (null) | 2016-01-31 |
You can use the following query:
SELECT t1.*, t3.*
FROM mytable AS t1
LEFT JOIN (
SELECT PHONE, MAX(date) AS max_date
FROM mytable
GROUP BY PHONE
) AS t2 ON t1.PHONE = t2.PHONE
LEFT JOIN mytable AS t3 ON t1.PHONE = t3.PHONE AND t2.max_date = t3.`date`
WHERE t1.description = 'main'
With a GROUP BY we'll first group on both PHONE and description, thus getting 4 rows.
Next we'll create a comma separated set using GROUP_CONCAT. It can take an ORDER BY clause, to order the phone numbers by date.
Last we want to get the first item from the set, we can do this with SUBSTRING_INDEX.
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(`PHONE` ORDER BY `DATE`), ',', 1) AS `PHONE`,
description
FROM table1
GROUP BY `PHONE`, `description`;
See Fiddle
Try the following query..
SELECT
A.*
FROM
`old_young` A
INNER JOIN
(SELECT
MIN(`DATE`) AS Res
FROM
old_young
WHERE description = 'main'
GROUP BY PHONE
UNION
ALL
SELECT
MAX(`DATE`) AS Res
FROM
old_young
WHERE description = ''
GROUP BY PHONE) B
ON A.DATE = B.Res ;
Check the FIDDLE

Select All customers with exact name and birthdate

I want to select all the customers that have the same name and birth date on mysql table.
my query right now is close, but it seems to have a flaw:
SELECT
id,
customer.name,
date
FROM
customer
INNER JOIN (
SELECT
name
FROM
customer
GROUP BY
name,date
HAVING
COUNT(id) > 1
) temp ON customer.name = customer.name
ORDER BY
name;
Return a customer if there EXISTS another one with same name and date, but other id:
SELECT
id,
name,
date
FROM
customer c1
where exists (SELECT 1 from customer c2
where c2.name = c1.name
and c2.date = c1.date
and c2.id <> c1.id)
JOIN version:
SELECT
c1.id,
c1.name,
c1.date
FROM
customer c1
JOIN customer c2
ON c2.name = c1.name
and c2.date = c1.date
and c2.id <> c1.id
You can do it using EXISTS expression:
SELECT
id,
customer.name,
date
FROM customer c
WHERE EXISTS (
SELECT *
FROM customer cc
WHERE cc.name=c.name AND cc.date=c.date AND cc.id <> c.id
)
The meaning of this query can be derived by pretending it's plain English: we are looking for all customers c for which there exists another customer cc with the same name and birth date, but different id.
Something like this should do it:
select
c1.*
from
customer c1
join customer c2 on
c1.name = c2.name
and c1.birth_date = c2.birth_date
and c1.id != c2.id
order by name, birth_date, id
;
And here's the full example:
drop table customer;
create table customer (
id int,
name varchar(64),
birth_date date
);
insert into customer values (1, 'Joe', '2001-01-01');
insert into customer values (2, 'Joe', '2001-01-02');
insert into customer values (3, 'Joe', '2001-01-03');
insert into customer values (4, 'Jim', '2001-01-01');
insert into customer values (5, 'Jack', '2001-01-01');
insert into customer values (6, 'George', '2001-01-01');
insert into customer values (7, 'George', '2001-01-02');
insert into customer values (8, 'Jeff', '2001-01-02');
insert into customer values (10, 'Joe', '2001-01-01');
insert into customer values (60, 'George', '2001-01-01');
select * from customer;
select
c1.*
from
customer c1
join customer c2 on
c1.name = c2.name
and c1.birth_date = c2.birth_date
and c1.id != c2.id
order by name, birth_date, id
;
+ ------- + --------- + --------------- +
| id | name | birth_date |
+ ------- + --------- + --------------- +
| 6 | George | 2001-01-01 |
| 60 | George | 2001-01-01 |
| 1 | Joe | 2001-01-01 |
| 10 | Joe | 2001-01-01 |
+ ------- + --------- + --------------- +
4 rows
Assuming a table with only id, name and date, you can expand it after that.
The solution is going to be in using an aliased join or an exists clause.
First some code to setup a temporary table with some values for us to query against.
--Drop Temporary Test Table if it exists
IF OBJECT_ID('tempdb.dbo.#Customers_Test', 'U') IS NOT NULL
DROP TABLE #Customers_Test;
--Create a Temporary Test Table
CREATE TABLE #Customers_Test
(
[id] [int] IDENTITY(1,1) NOT NULL,
[name] [varchar](50) NULL,
[date] [datetime] NULL,
CONSTRAINT [PK_Customers_Test] PRIMARY KEY CLUSTERED
(
[id] ASC
)
WITH
(
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
--Put some temporary values into the table
INSERT INTO #Customers_Test(name, [date])
VALUES ('Joe', GETUTCDATE()),
('Mark', GETDATE()),
('Dan', '1/1/1990'),
('Dan', '1/1/1990')
Now for the actual selection styles, there are several ways to do this depending on mostly preferences, if this query had become more complex there may have been a significant run time advantage of one over another.
--First Select Version
SELECT #Customers_Test.id,
#Customers_Test.name,
#Customers_Test.[date]
FROM #Customers_Test
WHERE EXISTS (
SELECT 1
FROM #Customers_Test Duped_Customers
WHERE Duped_Customers.name = #Customers_Test.name
AND Duped_Customers.[date] = #Customers_Test.[date]
AND Duped_Customers.id <> #Customers_Test.id
)
Another way:
--Second Select Version
SELECT #Customers_Test.id,
#Customers_Test.name,
#Customers_Test.[date]
FROM #Customers_Test
INNER JOIN #Customers_Test AS Duped_Customers
ON #Customers_Test.name = Duped_Customers.name
AND #Customers_Test.[date] = Duped_Customers.[date]
AND #Customers_Test.id <> Duped_Customers.id
One Final Way:
--Third Select Version ( Similar to your current logic).
SELECT #Customers_Test.id,
#Customers_Test.name,
#Customers_Test.[date]
FROM #Customers_Test
WHERE #Customers_Test.name IN (
SELECT name
FROM #Customers_Test Duped_Customers
GROUP BY name, [date]
HAVING COUNT(name) > 1
)

Find longest range of free ids in mysql, using a query?

This can certainly be done using a simple script and reading the database.
I am interested to know if the same is possible using some MySQL query.
Table Schema :
+--------+-------------------------------+
| doc_id | doc_title |
+--------+-------------------------------+
| 40692 | hello |
| 13873 | isaac |
| 37739 | einstei |
| 36042 | cricket |
| 96249 | astronaut |
| 81931 | discovery |
| 28447 | scooby |
| 99632 | popeye |
+--------+-------------------------------+
Here doc_id is a random number between 1 to 99999 , the distribution is sparse. I would want to know longest ( or all of the longest ) unused number ranges in my mysql table.
i.e. if 71000 to 83000 is the longest such range, there will be no record having doc_id lying between these two values.
create table documents ( id int, name varchar(255) );
insert into documents (id, name ) values
( 40692,'hello'),
( 13873,'isaac'),
( 37739,'einstei'),
( 36042,'cricket'),
( 96249,'astronaut'),
( 81931,'discovery'),
( 28447,'scooby'),
( 99632,'popeye')
select
d1.id,
min( d2.id ),
min( d2.id ) - d1.id as 'gap'
from
documents d1
join documents d2 on d2.id > d1.id
group by
d1.id
order by
3 desc;
Try this query -
SELECT start_doc_id, doc_id end_doc_id, delta FROM (
SELECT
doc_id,
#d start_doc_id,
IF(#d IS NULL, 0, doc_id - #d) delta,
#d:=doc_id
FROM
doc, (SELECT #d:= NULL) t
ORDER BY doc_id
) doc
ORDER BY delta DESC
How about something like this
SELECT t1.doc_id,
MAX(t1.doc_id-IFNULL(t2.doc_id,0)) AS difference
FROM `table` t1
LEFT JOIN `table` t2 ON t1.doc_id>t2.doc_id
LEFT JOIN `table` t3 ON (t1.doc_id>t3.doc_id AND t3.doc_id>t2.doc_id)
WHERE t3.doc_id IS NULL
GROUP BY t1.doc
ORDER BY difference DESC
I'd look for a different solution from Wolfgang's if your documents table gets big (hundreds of thousands or millions of records) because a join with "on d2.id > d1.id" is going to create a huge temporary table and take lots of time.
If you create the temporary table, you can do it quickly:
create table tdoc (id int, nextid int);
insert into tdoc (id) (select id from documents);
update tdoc join documents as d1
on d1.id = tdoc.id
set nextid =
(select id from documents
where id > tdoc.id order by id limit 1);
select id, max(nextid-id) from tdoc;
There's no self-join and no heavy crunching; this solution scales to a large documents table if necessary

Find differences in name-value pairs across version IDs

Say I have a table with 3 columns:version_id, name, value.
Conceptually, this table has a bunch of name-value pairs for each version_id.
How can I write a query that will show only the name value pairs of the top two version_ids where the name value pair is not the same across version-ids?
Additionally, I am wondering if there is a way to put the differing name-value pairs from the different version_ids side by side, or have the rows be right next to each other in the results.
Basically, I want like a diff of the two versions.
Example:
version_id name value
23459 jsLibrary2 JQuery_1_4_3
23459 jsLibrary1 CrossDomainAjax_1_0
23456 jsLibrary2 JQuery_1_4_2
23456 jsLibrary1 CrossDomainAjax_1_0
23456 groovyInclude2 GroovyUtilities
23454 jsLibrary2 JQuery_1_4_2
23454 jsLibrary1 CrossDomainAjax_1_0
23454 groovyInclude2 GroovyUtilities
Ideal query result:
23456 jsLibrary2 JQuery_1_4_2
23459 jsLibrary2 JQuery_1_4_3
23456 groovyInclude2 GroovyUtilities
23459 NULL NULL
Note that ideally it would note new name-value pairs (where the name doesn't exist in the smaller version_id) and deleted name-value pairs (where the name doesn't exist in the larger version_id)
I'm sure this can be simplified — or at least, I really hope it can — but:
SELECT name,
version_id_before,
( SELECT value
FROM property_history
WHERE name = t.name
AND version_id = version_id_before
) AS value_before,
( SELECT MIN(version_id)
FROM property_history
WHERE version_id > version_id_before
) AS version_id_after,
( SELECT value
FROM property_history
WHERE name = t.name
AND version_id =
( SELECT MIN(version_id)
FROM property_history
WHERE version_id > version_id_before
)
) AS value_after
FROM ( SELECT name,
CASE WHEN EXISTS
( SELECT 1
FROM property_history
WHERE name = ph1.name
AND version_id =
( SELECT MAX(version_id)
FROM property_history
)
)
THEN ( SELECT MAX(version_id)
FROM property_history ph2
WHERE NOT EXISTS
( SELECT 1
FROM property_history
WHERE name = ph1.name
AND version_id = ph2.version_id
AND value =
( SELECT value
FROM property_history
WHERE name = ph1.name
AND version_id =
( SELECT MAX(version_id)
FROM property_history
)
)
)
)
ELSE ( SELECT MAX(version_id)
FROM property_history
WHERE name = ph1.name
)
END AS version_id_before
FROM property_history ph1
GROUP
BY name
) AS t
WHERE version_id_before IS NOT NULL
;
(Disclaimer: tested only using your example data-set, for which it gives the result:
+----------------+-------------------+-----------------+------------------+--------------+
| name | version_id_before | value_before | version_id_after | value_after |
+----------------+-------------------+-----------------+------------------+--------------+
| groovyInclude2 | 23456 | GroovyUtilities | 23459 | NULL |
| jsLibrary2 | 23456 | JQuery_1_4_2 | 23459 | JQuery_1_4_3 |
+----------------+-------------------+-----------------+------------------+--------------+
I haven't made any effort to construct other data-sets to test it on.)
I think you'll need to use a couple of subqueries to get the desired results since you are looking for the first and second values. I'm assuming that the name is the 'key' that you have to group on, in which case something along these lines should work:
Select
firstVersion.firstVersionId,
firstVersionDetails.name as firstVersionName,
firstVersionDetails.value as firstVersionValue,
--second version values will be null if there is no second value
secondVersion.secondVersionId,
secondVersionDetails.name as secondVersionName, --always the same as firstVersionName because name is a key field
secondVersionDetails.value as secondVersionValue
From
(
Select
name,
Max(version_id) as firstVersionId
From versions
Group by name
) as firstVersion
join versions as firstVersionDetails--inner join because every name has a first version
on firstVersions.version_id = firstVersion.firstVersionId
left outer Join --outer join so we always get the first version and get the second version whenever there is one (in other words, does *not* limit data to names with at least 2 versions)
(
select
name,
Max(version_id) as secondVersionId
from versions
Group by name
) as secondVersion
on firstVersion.name=secondVersion.name
and secondVersion.version_id < firstVersion.firstVersionId --exclude the first version when calculating the 'max'. This is the part of the join that allows us to identify the second version
left outer join versions as secondVersionDetails --using outer join again so we don't limit our data to names with 2 versions
on secondVersion.secondVersionId = secondVersionDetails.version_id
Happy querying! :-)
How about this approach -
SELECT MAX(version_id) INTO #cur FROM tbl;
SELECT MAX(version_id) INTO #prev FROM tbl WHERE version_id < #cur;
SELECT name, #prev, MAX(IF(version_id = #prev, value, '')) AS prev_val, #cur, MAX(IF(version_id = #cur, value, '')) AS cur_val
FROM tbl
WHERE version_id IN (#prev, #cur)
GROUP BY name
HAVING cur_val <> prev_val;