Using MySQL 5.5.30, for a table, purchase_order_product (MyISAM), that has the following records:
+----------+------------------+
| order_id | order_product_id |
+----------+------------------+
| 98 | 1 |
| 99 | 14 |
| 99 | 15 |
| 100 | 16 |
| 100 | 17 |
| 100 | 18 |
| 101 | 19 |
| 102 | 20 |
+----------+------------------+
When I run the following query from MySQL Workbench 5.2.47 CE:
SELECT pop.order_id, pop.order_product_id,
#RUNNING:=IF(#PREVIOUS = pop.order_id, #RUNNING, 0) + 1 AS rownum,
#PREVIOUS:=pop.order_id as previd
FROM purchase_order_product pop
ORDER BY pop.order_id , pop.order_product_id ASC;
I get the following output:
+----------+------------------+--------+--------+
| order_id | order_product_id | rownum | previd |
+----------+------------------+--------+--------+
| 98 | 1 | 1 | 98 |
| 99 | 14 | 1 | 99 |
| 99 | 15 | 2 | 99 |
| 100 | 16 | 1 | 100 |
| 100 | 17 | 2 | 100 |
| 100 | 18 | 3 | 100 |
| 101 | 19 | 1 | 101 |
| 102 | 20 | 1 | 102 |
+----------+------------------+--------+--------+
My desired goal is the value of rownum, which is fine and as expected so far...
HOWEVER, when I run the query from within my PHP code OR from the mysql command line against the same database using the same db user, I get the following output:
+----------+------------------+--------+--------+
| order_id | order_product_id | rownum | previd |
+----------+------------------+--------+--------+
| 98 | 1 | 1 | 98 |
| 99 | 14 | 1 | 99 |
| 99 | 15 | 1 | 99 |
| 100 | 16 | 1 | 100 |
| 100 | 17 | 1 | 100 |
| 100 | 18 | 1 | 100 |
| 101 | 19 | 1 | 101 |
| 102 | 20 | 1 | 102 |
+----------+------------------+--------+--------+
As you can see, rownum is always 1!
I have search extensively for a solution to this perplexing issue, to no avail. Does anyone have any idea what may be going on? Am I doing something wrong?
You need to initialize the variable:
SET #RUNNING:=0;
SET #PREVIOUS:=0;
This must be done before your query and in the same mysql session.
Related
This is the question:
To find
units in stock of a product, use the InventoryTransactions table, find total quantity
purchased and subtract quantities sold and on hold.
The Inventory Transaction table:
This are the transaction type for each transaction id:
transaction id 1 = purchased, trasaction id 2 = sold, transaction id 3 = on hold
+----+-----------+
| ID | TypeName |
+----+-----------+
| 1 | Purchased |
| 2 | Sold |
| 3 | On Hold |
| 4 | Waste |
+----+-----------+
4 rows in set (0.00 sec)
The product id, quantity, and type of transaction:
SELECT productid, quantity, transactiontype FROM Inventory_Transactions ORDER BY productid;
+-----------+----------+-----------------+
| productid | quantity | transactiontype |
+-----------+----------+-----------------+
| 1 | 40 | 1 |
| 1 | 15 | 2 |
| 1 | 25 | 3 |
| 3 | 100 | 1 |
| 3 | 50 | 2 |
| 4 | 40 | 1 |
| 4 | 10 | 2 |
| 4 | 30 | 2 |
| 5 | 40 | 1 |
| 5 | 25 | 2 |
| 6 | 100 | 1 |
| 6 | 10 | 2 |
| 6 | 90 | 2 |
| 7 | 40 | 1 |
| 7 | 10 | 2 |
| 7 | 30 | 2 |
| 8 | 40 | 1 |
| 8 | 17 | 2 |
| 8 | 25 | 1 |
| 8 | 25 | 2 |
| 8 | 20 | 2 |
| 8 | 3 | 2 |
| 14 | 40 | 1 |
| 17 | 40 | 1 |
| 17 | 40 | 2 |
| 19 | 20 | 1 |
| 19 | 20 | 2 |
| 19 | 30 | 1 |
| 19 | 30 | 2 |
| 19 | 25 | 1 |
| 19 | 10 | 2 |
| 19 | 10 | 1 |
| 19 | 25 | 2 |
| 20 | 40 | 1 |
| 20 | 40 | 2 |
| 21 | 20 | 1 |
| 21 | 20 | 2 |
| 34 | 60 | 1 |
| 34 | 100 | 1 |
| 34 | 100 | 2 |
| 34 | 12 | 3 |
| 34 | 10 | 3 |
| 34 | 1 | 3 |
| 34 | 50 | 1 |
| 34 | 300 | 1 |
| 34 | 300 | 2 |
| 34 | 87 | 2 |
| 40 | 120 | 1 |
| 40 | 50 | 2 |
| 40 | 30 | 2 |
| 40 | 40 | 2 |
| 41 | 40 | 1 |
| 41 | 200 | 1 |
| 41 | 200 | 2 |
| 41 | 30 | 2 |
| 41 | 50 | 1 |
| 41 | 50 | 2 |
| 41 | 10 | 2 |
| 43 | 100 | 1 |
| 43 | 20 | 2 |
| 43 | 300 | 1 |
| 43 | 300 | 2 |
| 43 | 25 | 3 |
| 43 | 250 | 1 |
| 43 | 300 | 3 |
| 43 | 5 | 2 |
| 48 | 100 | 1 |
| 48 | 10 | 2 |
| 48 | 100 | 1 |
| 48 | 100 | 2 |
| 48 | 10 | 2 |
| 48 | 40 | 2 |
| 48 | 40 | 2 |
| 51 | 40 | 1 |
| 51 | 10 | 2 |
| 51 | 30 | 2 |
| 52 | 100 | 1 |
| 52 | 40 | 2 |
| 56 | 120 | 1 |
| 56 | 110 | 3 |
| 57 | 80 | 1 |
| 57 | 100 | 1 |
| 57 | 100 | 2 |
| 65 | 40 | 1 |
| 66 | 80 | 1 |
| 72 | 40 | 1 |
| 72 | 50 | 1 |
| 72 | 50 | 2 |
| 72 | 40 | 2 |
| 74 | 20 | 1 |
| 74 | 20 | 2 |
| 77 | 60 | 1 |
| 80 | 75 | 1 |
| 80 | 30 | 2 |
| 80 | 10 | 2 |
| 80 | 20 | 3 |
| 80 | 15 | 2 |
| 81 | 125 | 1 |
| 81 | 200 | 1 |
| 81 | 200 | 2 |
| 81 | 50 | 3 |
| 81 | 25 | 3 |
+-----------+----------+-----------------+
102 rows in set (0.00 sec)
I will need to calculate unit of stock for each productid by SUM(quantity purchased) - SUM(quantity sold) - SUM(quantity on hold)
My take:
CREATE VIEW purchased AS
SELECT productid, SUM(quantity) quantity
FROM Inventory_Transactions
WHERE transactiontype = 1
GROUP BY productid
ORDER BY productid;
CREATE VIEW sold AS
SELECT productid, SUM(quantity) quantity
FROM Inventory_Transactions
WHERE transactiontype = 2
GROUP BY productid
ORDER BY productid;
CREATE VIEW onhold AS
SELECT productid, SUM(quantity) quantity
FROM Inventory_Transactions
WHERE transactiontype = 3
GROUP BY productid
ORDER BY productid;
SELECT pur.productid, pur.quantity - so.quantity - on.quantity
FROM purchased pur, sold so, onhold on
WHERE pur.productid = so.productid = on.productid;
But I got this error
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'on WHERE pur.productid = so.productid = on.productid' at line 1
These are the individual VIEW I created above:
SELECT * FROM purchased;
+-----------+----------+
| productid | quantity |
+-----------+----------+
| 1 | 40 |
| 3 | 100 |
| 4 | 40 |
| 5 | 40 |
| 6 | 100 |
| 7 | 40 |
| 8 | 65 |
| 14 | 40 |
| 17 | 40 |
| 19 | 85 |
| 20 | 40 |
| 21 | 20 |
| 34 | 510 |
| 40 | 120 |
| 41 | 290 |
| 43 | 650 |
| 48 | 200 |
| 51 | 40 |
| 52 | 100 |
| 56 | 120 |
| 57 | 180 |
| 65 | 40 |
| 66 | 80 |
| 72 | 90 |
| 74 | 20 |
| 77 | 60 |
| 80 | 75 |
| 81 | 325 |
+-----------+----------+
28 rows in set (0.00 sec)
SELECT * FROM sold;
+-----------+----------+
| productid | quantity |
+-----------+----------+
| 1 | 15 |
| 3 | 50 |
| 4 | 40 |
| 5 | 25 |
| 6 | 100 |
| 7 | 40 |
| 8 | 65 |
| 17 | 40 |
| 19 | 85 |
| 20 | 40 |
| 21 | 20 |
| 34 | 487 |
| 40 | 120 |
| 41 | 290 |
| 43 | 325 |
| 48 | 200 |
| 51 | 40 |
| 52 | 40 |
| 57 | 100 |
| 72 | 90 |
| 74 | 20 |
| 80 | 55 |
| 81 | 200 |
+-----------+----------+
23 rows in set (0.01 sec)
SELECT * FROM onhold;
+-----------+----------+
| productid | quantity |
+-----------+----------+
| 1 | 25 |
| 34 | 23 |
| 43 | 325 |
| 56 | 110 |
| 80 | 20 |
| 81 | 75 |
+-----------+----------+
6 rows in set (0.00 sec)
I am using the northwind database
Here are all the table relationship in northwind database.
May I know how do I solve this?
The approach I would take is using something called "Conditional aggregation"
use a summed case expression to determine if it's a purchase
use a summed 2nd case expression to determine if it's a sale/hold
subtract the two
group by product
.
SELECT ProductID,
SUM(case when TransactionType = 1 then quantity else 0 end) -
SUM(case when TransactionType in (2,3) then quantity else 0 end) as AvailableInventory
FROM Inventory_Transactions
GROUP BY ProductID
As to the nature of your error:
you have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'on WHERE pur.productid = so.productid = on.productid' at line 1
on is a reserved word use onh or onhold or something for the alias
using , joins is and old technique use Inner, outer, full outer, cross join and use the on notation to specify how he tables relate
equality checks within where clauses must return true false. To date they are not trinary. If A=B but <> C what would you expect to happen? so break out your productID's so they handle all the needed joins.
you're going to have problems of matching records because not all 3 may have a value so if you match on productID for sold or hold, you may not have a product and it would fall out of your results... thus outer joins are needed if you continue with your current approach.
.
ORIGINAL
SELECT pur.productid, pur.quantity - so.quantity - on.quantity
FROM purchased pur, sold so, onhold on
WHERE pur.productid = so.productid = on.productid;
should be:
SELECT coalesce(pur.productid, so.productid,onhold.ProductID) as productid,
coalesce(pur.quantity,0) - coalesce(so.quantity,0) -
coalesce(onhold.quantity) as AvailableInventory
FROM purchased pur
FULL OUTER JOIN sold so
on pur.productid = so.productid
FULL OUTER JOIN onhold
on pur.productid = onhold.productid
OR so.productid = onhold.productid
GROUP BY coalesce(pur.productid, so.productid,onhold.ProductID);
Breaking this down
the coalesce of productID is because we don't know in which of the 1,2,3 a productID will exist. But, we need the product id to show up for any of the 3 not just purchases.
the coalesce on quantity is because if we try to subtract a NULL value, we get a NULL so we need to ensure a numeric value exists. pretend for a second product ID 1 exists in purchases but has had no sales or holds. 10 purchases - NULL = NULL... not what we want. now if the productID 2 is in sales but not purchases we'd have NULL-10-NULL... again not what we want. Also if we just used the productid from purchases it would be NULL on this record...; also not good.
we replaced the , notation which is a cross join to a full outer so we don't lose records. Now cross join would work but it takes # of purchases * #of sales * number of on hold then limits the records based on your where clause this is WAY more work than the database needs to do and on a large dataset would be VERY slow.
Discussed earlier, where clause can't be trinary it must be a binary result comparing just 2 values.
Now full outer vs left vs inner vs right....
FULL OUTER: include all records from both tables and line them up where they match on Key (productID in our example)
LEFT JOIN include all records from the first table and any that match from the 2nd
INNER Include only records which exist in both tables.
RIGHT include all records from the 2nd table and any that match from the 1st.
CROSS JOIN: all records related to all records (Very slow but there are uses; just not here)
and more...
If we can't assume each of your products has a value in each group: pur, sold, onhold; we need to use full outer joins as above; so we don't exclude any records. However even this can be an issue because we have to ensure each productID ties back to a related product in another table if it exists.
To do this we have to use an OR on the last match (kinda ugly) and as we don't know the source of productID... we have to find one using coalesce.
If we can assume all products must first exist as a purchase, we can left join the other two. A bit cleaner, but we still have to handle the situation where there may be no data in sold or on hold, so we have to coalesce the 0 in for the quantities. we don't have to coalesce the productid since we know a purchase has to exist.
SELECT pur.productid, sum(coalesce(pur.quantity,0) - coalesce(so.quantity,0) -
coalesce(onhold.quantity)) as AvailableInventory
FROM purchased pur
LEFT OUTER JOIN sold so
on pur.productid = so.productid
LEFT OUTER JOIN onhold
on pur.productid = onhold.productid
--Note: we tie back to pur both times as we know it exists there.
GROUP BY pur.productid;
Hopefully you can see the conditional aggregation is the better approach as it simplifies and reduces joins and avoids much of the coalesing we do.
I have a table like this:
mysql> select * from studentscore;
+------------+-----------+-------+
| student_id | cource_id | score |
+------------+-----------+-------+
| 1 | 1 | 80 |
| 1 | 2 | 90 |
| 1 | 3 | 85 |
| 1 | 4 | 78 |
| 2 | 2 | 53 |
| 2 | 3 | 77 |
| 2 | 5 | 80 |
| 3 | 1 | 71 |
| 3 | 2 | 70 |
| 3 | 4 | 80 |
| 3 | 5 | 65 |
| 3 | 6 | 75 |
| 4 | 2 | 90 |
| 4 | 3 | 80 |
| 4 | 4 | 70 |
| 4 | 6 | 95 |
| 5 | 1 | 60 |
| 5 | 2 | 70 |
| 5 | 5 | 80 |
| 5 | 6 | 69 |
| 6 | 1 | 76 |
| 6 | 2 | 88 |
| 6 | 3 | 87 |
| 7 | 4 | 80 |
| 8 | 2 | 71 |
| 8 | 3 | 58 |
| 8 | 5 | 68 |
| 9 | 2 | 88 |
| 10 | 1 | 77 |
| 10 | 2 | 76 |
| 10 | 3 | 80 |
| 10 | 4 | 85 |
| 10 | 5 | 83 |
| 11 | 3 | 80 |
| 12 | 4 | 99 |
| 13 | 5 | 74 |
+------------+-----------+-------+
I want to show student_id and students' average scores that are higher than 80.
The output I want is like this:
+------------+-------------------+
| student_id | Average |
+------------+-------------------+
| 1 | 83.25 |
| 4 | 83.75 |
| 6 | 83.66666666666667 | // and how can I make this result shorter like 83.67?
| 7 | 80 |
| 9 | 88 |
| 10 | 80.2 |
| 11 | 80 |
| 12 | 99 |
+------------+-------------------+
I've tried the following codes
mysql> select student_id, avg(score) as average_score
-> from studentscore
-> group by student_id
-> where avg(score) >= 80;
and it gave me an syntax error.
I know by rules the where clause should go before the group by clause but I can't because the where clause depends on the result from the group by clause, and if I switch their position it will give me another error("Invalid use of group function").
Can some one tell me how to get the table I want?
use "having" instead of "where"
use having instead of where.
Here's the difference:
with where you can write a predicate that will be applied to each row
with having you can write a predicate that will applied to each group
and in your case, the 2nd is the only solution that can work.
select student_id, avg(score) as average_score
from studentscore
group by student_id
having avg(score) >= 80;
where applies a filter to your data before grouping has taken place, whereas having applies a filter post-grouping. round(,2) will format as you also ask:
select student_id, round(avg(score), 2) as average_score
from studentscore
group by student_id
having average_score >= 80;
There is a rowset like this:
| ID | OP_CODE | OWNER | MEASURE | COUNT |
|----|-------------|-------|----------|-------|
| 1 | Operation 1 | 1 | Geometry | 42 |
| 2 | Operation 1 | 1 | Geometry | 48 |
| 3 | Operation 1 | 1 | Vacuum | 29 |
| 4 | Operation 1 | 1 | Electro | 14 |
| 5 | Operation 1 | 2 | Geometry | 87 |
| 6 | Operation 1 | 2 | Geometry | 112 |
| 7 | Operation 1 | 2 | Vacuum | 78 |
| 8 | Operation 1 | 3 | Vacuum | 56 |
| 9 | Operation 1 | 3 | Electro | 78 |
I want to group rows by Owner and merge/join other Measures (values of column MEASURE) with sum of column Count to this result like this:
| OWNER | GEOMETRY_CNT | VACUUM_CNT | ELECTRO_CNT | TOTAL_CNT |
|-------|--------------|------------|-------------|-----------|
| 1 | 90 | 29 | 14 | 133 |
| 2 | 199 | 78 | (null) | 277 |
| 3 | (null) | 56 | 78 | 134 |
In this case Geometry_cnt, Vacuum_cnt, Electro_cnt is a sum of corresponding values in first table:
Owner_1_Geometry_cnt=42+48=90;
Owner_1_Vacuum_cnt=29;
Owner_1_Electro_cnt=14;
Owner_1_TOTAL=29+14+90=133;
How can I get this rowset?
SQL Fiddle
Try this:
SELECT a.OWNER, SUM(IF(a.MEASURE = 'Geometry', a.COUNT, 0)) GEOMETRY_CNT,
SUM(IF(a.MEASURE = 'Vacuum', a.COUNT, 0)) VACUUM_CNT,
SUM(IF(a.MEASURE = 'Electro', a.COUNT, 0)) ELECTRO_CNT,
SUM(a.COUNT) TOTAL_CNT
FROM operations_schedule a
GROUP BY a.OWNER
Check the SQL FIDDLE DEMO
OUTPUT
| OWNER | GEOMETRY_CNT | VACUUM_CNT | ELECTRO_CNT | TOTAL_CNT |
|-------|--------------|------------|-------------|-----------|
| 1 | 90 | 29 | 14 | 133 |
| 2 | 199 | 78 | 0 | 277 |
| 3 | 0 | 56 | 78 | 134 |
good morning. I have this table:
mysql> select * from Data;
+---------------------------+--------+-------+
| affyId | exptId | level |
+---------------------------+--------+-------+
| 31315_at | 3 | 250 |
| 31324_at | 3 | 91 |
| 31325_at | 1 | 191 |
| 31325_at | 2 | 101 |
| 31325_at | 4 | 51 |
| 31325_at | 5 | 71 |
| 31325_at | 6 | 31 |
| 31356_at | 3 | 91 |
| 31362_at | 3 | 260 |
| 31510_s_at | 3 | 257 |
| 5321_at | 4 | 90 |
| 5322_at | 4 | 90 |
| 5323_at | 4 | 90 |
| 5324_at | 3 | 57 |
| 5324_at | 4 | 90 |
| 5325_at | 4 | 90 |
| AFFX-BioB-3_at | 3 | 97 |
| AFFX-BioB-5_at | 3 | 20 |
| AFFX-BioB-M_at | 3 | 20 |
| AFFX-BioB-M_at | 5 | 214 |
| AFFX-BioB-M_at | 7 | 20 |
| AFFX-BioB-M_at | 8 | 40 |
| AFFX-BioB-M_at | 9 | 20 |
| AFFX-HSAC07/X00351_M_at | 3 | 86 |
| AFFX-HUMBAPDH/M33197_3_st | 3 | 277 |
| AFFX-HUMTFFR/M11507_at | 3 | 90 |
| AFFX-M27830_3_at | 3 | 271 |
| AFFX-MurIL10_at | 3 | 8 |
| AFFX-MurIL10_at | 5 | 8 |
| AFFX-MurIL10_at | 6 | 4 |
| AFFX-MurIL2_at | 3 | 20 |
| AFFX-MurIL4_at | 5 | 78 |
| AFFX-MurIL4_at | 6 | 20 |
| U95-32123_at | 1 | 128 |
| U95-32123_at | 2 | 128 |
| U98-40474_at | 1 | 57 |
| U98-40474_at | 2 | 57 |
+---------------------------+--------+-------+
37 rows in set (0.00 sec)
If I wanna look for the average expression level (level) of each array probe (affyId) across all experiments, I do SELECT affyId, AVG(level) AS average FROM Data GROUP BY affyId;
However, I can't figure out how to look for the average expression level of each array probe (affyId) for each experiment... It must be something similar to the last query, but I don't obtain good results... any help?
PD: someone told me I should give some reputation or click to some green button if somebody solves my question... Is it right? How do I do it? I'm pretty new on this website...
This shows the average for every affyId:
SELECT affyId, AVG(level) AS average FROM Data GROUP BY affyId
This the average for every exptId:
SELECT exptId, AVG(level) AS average FROM Data GROUP BY exptId
and this the average for every exptId in every affyId:
SELECT affyId, exptId, AVG(level) AS average FROM Data GROUP BY exptId, affyId
Just add that to the group by clause
SELECT affyId, exptId, AVG(level) AS average
FROM Data
GROUP BY affyId, exptId;
I have my table transfer
| id | fix | part | created |
| + | + | + | + |
| + | + | + | + |
| 238 | 1 | 1 | 2012-02-10 21:15:48 |
| 239 | 9 | 1 | 2012-02-11 12:36:17 |
| 240 | 1 | 2 | 2012-02-12 23:35:28 |
| 241 | 2 | 1 | 2012-02-13 06:17:35 |
| 242 | 4 | 2 | 2012-02-14 17:45:42 |
| 243 | 1 | 1 | 2012-02-15 20:32:58 |
| 244 | 2 | 2 | 2012-02-16 12:52:19 |
| 245 | 3 | 1 | 2012-02-17 22:35:56 |
| 246 | 1 | 2 | 2012-02-18 09:11:23 |
| 247 | 3 | 1 | 2012-02-19 19:46:44 |
| 248 | 1 | 1 | 2012-02-20 02:30:14 |
| 249 | 2 | 1 | 2012-02-21 13:36:49 |
| 250 | 1 | 3 | 2012-02-22 21:35:34 |
| 251 | 1 | 1 | 2012-02-23 19:25:12 |
| 252 | 1 | 2 | 2012-02-24 18:53:43 |
| 253 | 1 | 3 | 2012-02-25 21:05:28 |
| 254 | 3 | 1 | 2012-02-26 12:33:35 |
| 255 | 1 | 1 | 2012-02-27 18:35:18 |
| 256 | 4 | 1 | 2012-02-28 22:15:27 |
| 257 | 4 | 1 | 2012-03-01 12:22:17 |
| 258 | 2 | 2 | 2012-03-02 10:19:24 |
| 259 | 9 | 1 | 2012-03-03 18:45:46 |
| 260 | 1 | 2 | 2012-03-04 23:19:07 |
| 261 | 2 | 1 | 2012-03-05 09:11:11 |
| 262 | 1 | 1 | 2012-03-06 21:25:29 |
| + | + | + | + |
| + | + | + | + |
| 901 | 1 | 3 | 2012-04-30 22:15:27 |
| 902 | 3 | 1 | 2012-05-01 12:22:17 |
| 903 | 2 | 1 | 2012-05-02 10:19:24 |
| 904 | 1 | 1 | 2012-05-03 18:45:46 |
| + | + | + | + |
| + | + | + | + |
and want to keep the latest (created) 3 rows of every same fix and part column with a interval of.. for example 3 month. If there a only 1 or 2 rows then show them! (see id 242 for example)
I tried a couple of things and searching on stackoverflow, but I didn't find a solution with the extra column (part).
The table transfer should look like this after process:
| id | fix | part | created |
| 904 | 1 | 1 | 2012-05-03 18:45:46 |
| 262 | 1 | 1 | 2012-03-06 21:25:29 |
| 255 | 1 | 1 | 2012-02-27 18:35:18 |
| 260 | 1 | 2 | 2012-03-04 23:19:07 |
| 252 | 1 | 2 | 2012-02-24 18:53:43 |
| 246 | 1 | 2 | 2012-02-18 09:11:23 |
| 901 | 1 | 3 | 2012-04-30 22:15:27 |
| 253 | 1 | 3 | 2012-02-25 21:05:28 |
| 250 | 1 | 3 | 2012-02-22 21:35:34 |
| 903 | 2 | 1 | 2012-05-02 10:19:24 |
| 261 | 2 | 1 | 2012-03-05 09:11:11 |
| 249 | 2 | 1 | 2012-02-21 13:36:49 |
| 258 | 2 | 2 | 2012-03-02 10:19:24 |
| 244 | 2 | 2 | 2012-02-16 12:52:19 |
| 902 | 3 | 1 | 2012-05-01 12:22:17 |
| 254 | 3 | 1 | 2012-02-26 12:33:35 |
| 247 | 3 | 1 | 2012-02-19 19:46:44 |
| 257 | 4 | 1 | 2012-03-01 12:22:17 |
| 256 | 4 | 1 | 2012-02-28 22:15:27 |
| 242 | 4 | 2 | 2012-02-14 17:45:42 |
| 259 | 9 | 1 | 2012-03-03 18:45:46 |
| 239 | 9 | 1 | 2012-02-11 12:36:17 |
I ordered the example by fix and part for better understanding.
Maybe someone can give me a hint?
You might try something like this:
delete from transfer
where id not in (
select id
from (
select id, fix, part, created,
(select count(*) from transfer where created >= a.created and fix = a.fix and part = a.part) as rank
from transfer a
) as t
where rank <= 3
);
I'm using a correlated subquery to rank the rows, and this will have the inherent performance flaws of a triangle join (SQL Server article, but stil applies).
Here is a SqlFiddle demo.
You can get the list of ids to be removed by using:
select t.*,
group_concat(id separator ',' order by created desc) as ids
from transfer
where created >= curdate - interval 3 month
group by fix, part
You can convert this to a delete as:
delete from t
where not exists (select 1
from (select fix, part,
group_concat(id separator ',' order by created desc) as ids
from transfer
where created >= curdate - interval 3 month
group by fix, part
) t1
where t.id not in (substring_index(ids, 1), substring_index(ids, 2), substring_index(ids, 3))
)
Although MySQL is fussy about using the same table for the delete in subqueries, it does allow it with multiple levels of subqueries.