I need to find the missing numbers between 0 and 16.
My table is like this:
CarId FromCity_Id ToCity_Id Ran_Date RunId
1001 0 2 01-08-2013 1
1001 5 9 02-08-2013 2
1001 11 16 03-08-2013 3
1002 0 11 02-08-2013 4
1002 11 16 08-08-2013 5
I need to find out:
In past three months from now(), between which cities the car has not ran.
For example, in the above records:
Over all is that, I need to generate a query which shows the section between which the car has not run in past 3 months with showing the last run date.
How to make such an query please. If any Stored Procedure please advise.
God help me. This uses a doubly-correlated subquery, a table that might not exist in your system, and too much caffeine. But hey, it works.
Right, here goes.
SELECT CarId, GROUP_CONCAT(DISTINCT missing) missing
FROM MyTable r,
(SELECT @a := @a + 1 missing
FROM mysql.help_relation, (SELECT @a := -1) t
WHERE @a < 16 ) y
WHERE NOT EXISTS
(SELECT r.CarID FROM MyTable m
WHERE y.missing BETWEEN FromCity_Id AND ToCity_Id
AND r.carid = m.carid)
GROUP BY CarID;
Produces (changing the first row for CarID 1002 to 0-9 to open up 10 and give us better test data):
+-------+---------+
| CarId | missing |
+-------+---------+
| 1001 | 3,4,10 |
| 1002 | 10 |
+-------+---------+
2 rows in set (0.00 sec)
And how does it all work?
Firstly... The inner query gives us a list of numbers from 0 to 16:
(SELECT @a := @a + 1 missing
FROM mysql.help_relation, (SELECT @a := -1) t
WHERE @a < 16 ) y
It does that by starting at -1, and then displaying the result of adding 1 to that number for each row in some sacrificial table. I'm using mysql.help_relation because it's got over a thousand rows and most basic systems have it. YMMV.
Then we cross join that with MyTable:
SELECT CarId, ...
FROM MyTable r,
(...) y
This gives us every possible combination of rows, so we have each CarId and To/From IDs mixed with every number from 1-16.
Filtering...
This is where it gets interesting. We need to find rows that don't match the numbers, and we need to do so per CarID. This sort of thing would do it (as long as y.missing exists, which it will when we correlate the subquery):
SELECT m.CarID FROM MyTable m
WHERE y.missing BETWEEN FromCity_Id AND ToCity_Id
AND m.CarID = 1001;
Remember: y.missing is set to a number between 1-16, cross-joined with the rows in MyTable. This gives us a list of all numbers from 1-16 where CarID 1001 is busy. We can invert that set with a NOT EXISTS, and while we're at it, correlate (again) with CarId so we can get all such IDs.
Then it's an easy matter of filtering the rows that don't fit:
SELECT CarId, ...
FROM MyTable r,
(...) y
WHERE NOT EXISTS
(SELECT r.CarID FROM MyTable m
WHERE y.missing BETWEEN FromCity_Id AND ToCity_Id
AND r.carid = m.carid)
Output To give a sensible result (attempt 1), we could then get distinct combinations. Here's that version:
SELECT DISTINCT CarId, missing
FROM MyTable r,
(SELECT @a := @a + 1 missing
FROM mysql.help_relation, (SELECT @a := -1) t
WHERE @a < 16 ) y
WHERE NOT EXISTS
(SELECT r.CarID FROM MyTable m
WHERE y.missing BETWEEN FromCity_Id AND ToCity_Id
AND r.carid = m.carid);
This gives:
+-------+---------+
| CarId | missing |
+-------+---------+
| 1001 | 3 |
| 1001 | 4 |
| 1001 | 10 |
| 1002 | 10 |
+-------+---------+
4 rows in set (0.01 sec)
The simple addition of a GROUP BY and a GROUP CONCAT gives the pretty result you get at the top of this answer.
I apologise for the inconvenience.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With