2013-11-25 62 views
4

故事是這樣的...我有Users,他們有Children。 我想每天使用CRON JOB優惠券發送給有子女出生日期間隔的用戶。 我想知道誰將是用戶獲得優惠券和哪個孩子。 我也想爲每個孩子只發送一張優惠券,孩子必須是用戶擁有的最小的優惠券。GROUP BY和ORDER BY在聯合表 - 複雜和緩慢

我有以下表

Children 
+--------------------------------------+ 
- Primary Key: childrenID (int) 
- Index: userID (int) 
- Index: childBirthDate (date) 
+--------------------------------------+ 
- childrenID - userID - childBirthDate - 
- 1   - 1  - 21/01/2000  - 
- 2   - 1  - 01/11/2013  - 
- 3   - 1  - 25/10/2013  - 
- 4   - 2  - 01/11/2013  - 
- 5   - 3  - 01/11/2013  - 
+--------------------------------------+ 

Users 
+------------------------+ 
- Primary Key: userID (int) 
- Index: categoryGroup (varchar) 
+------------------------+ 
- userID - categoryGroup - 
- 1  - 'Group1'  - 
- 2  - 'Group1'  - 
- 3  - 'Group2'  - 
- 4  - 'Group2'  - 
+------------------------+ 

CuponRequests 
+------------------------+ 
- Primary Key: ID (int) 
- Index: userID (int) 
- Index: cuponID (int) 
+-----------------------+ 
- ID - cuponID - userID - 
- 1 - 1  - 1  - 
- 1 - 2  - 1  - 
- 1 - 1  - 2  - 
+-----------------------+ 

這基本上是與相關列 三個主要的表,我有以下SQL查詢來執行,並獲取我需要的結果。

SELECT users.userID, 
     users.categoryGroup children.childBirthDate, 
     children.childrenID 
FROM users, 
    (SELECT * 
    FROM 
    (SELECT children.childrenID, 
      children.childBirthDate, 
      users.userID AS child_uid 
     FROM children, 
      users 
     WHERE children.userID = users.userID 
     ORDER BY children.childBirthDate DESC)t1 
    GROUP BY child_uid)children 
WHERE (children.childBirthDate <= DATE_SUB(CURDATE(), INTERVAL 5 MONTH)) 
    AND (children.childBirthDate > DATE_SUB(CURDATE() , INTERVAL 6 MONTH)) 
    AND (children.child_uid = users.userID) 
    AND ('Group1, Group2' LIKE CONCAT('%', users.categoryGroup, '%')) 
    AND NOT EXISTS 
    (SELECT userID, 
      cuponID 
    FROM cuponRequests 
    WHERE userID = users.userID 
     AND cuponID = 1) 
    AND userID = 1 
ORDER BY children.childBirthDate DESC 

對於此查詢我想只有一個用戶,並只在一個優惠券 但它的工作是理所當然的行爲 - 查詢正在對所有的用戶

的「cuponID」,和間隔,來自腳本的PHP端 - 我迭代「cupons」表(這裏沒有提到),並在每個「優惠券」行上執行此查詢)

問題是此查詢正在執行1.5秒(OO) 除了在CRON JOB環境中運行此腳本外,此腳本也運行在用戶註冊到網站之後。我有96個cupons - 這使得減慢登記約1分鐘(這是一個很大)


我盤算了一下,這個查詢

SELECT * 
FROM 
    (SELECT children.childrenID, 
      children.childBirthDate, 
      users.userID AS child_uid 
    FROM children, 
     users 
    WHERE children.userID = users.userID 
    ORDER BY children.childBirthDate DESC)t1 
GROUP BY child_uid 

會減慢速度。我試圖做的,而不是一個JOIN關選擇查詢這樣的選擇查詢裏面:

FROM users LEFT JOIN children ON children.userID = users.userID 

但後來我失去了「ORDER BY childBirthDate DESC」來獲得該用戶的最小的孩子,我失去了「GROUP BY child_uid「只得到他的一個孩子

任何想法如何使事情更快,但仍然工作?

P.S 對不起,我缺乏英語。


編輯:

這裏是輸出EXPLAIN SQL

+----+--------------------+---------------+-------+----------------+---------+---------+------------------------------+-------+-----------------------------------------------------+ 
| id | select_type  |  table  | type | possible_keys | key | key_len |    ref    | rows |      Extra      | 
+----+--------------------+---------------+-------+----------------+---------+---------+------------------------------+-------+-----------------------------------------------------+ 
| 1 | PRIMARY   | NULL   | NULL | NULL   | NULL | NULL | NULL       | NULL | Impossible WHERE noticed after reading const tables | 
| 4 | DEPENDENT SUBQUERY | cuponRequests | ref | userID,cuponID | userID | 5  | const      | 1  | Using where           | 
| 2 | DERIVED   | <derived3> | ALL | NULL   | NULL | NULL | NULL       | 73526 | Using temporary; Using filesort      | 
| 3 | DERIVED   | users   | index | PRIMARY  | PRIMARY | 4  | NULL       | 69271 | Using index; Using temporary; Using filesort  | 
| 3 | DERIVED   | children  | ref | userID   | userID | 4  | users.userID     | 1  |              | 
+----+--------------------+---------------+-------+----------------+---------+---------+------------------------------+-------+-----------------------------------------------------+ 
+2

什麼是您的INDEX?如果有的話 – nrathaus

+1

您的日期是否使用DATE數據類型存儲?另外,「我想只爲每個孩子發送一個cupon,並且孩子必須是用戶擁有的最老的。」 - 最古老的可讀孩子,還是最古老的孩子,完全停下來? – Strawberry

+0

爲問題添加了索引 –

回答

1

這個查詢要快很多。我已經提出了有關出生日期的條件。

SELECT * 
FROM 
    (SELECT children.childrenID, 
      children.childBirthDate, 
      users.userID AS child_uid 
    FROM children, 
     users 
    WHERE children.userID = users.userID 
    AND children.childBirthDate <= DATE_SUB(CURDATE(), INTERVAL 5 MONTH) 
    AND children.childBirthDate > DATE_SUB(CURDATE() , INTERVAL 6 MONTH) 
    ORDER BY children.childBirthDate DESC)t1 
GROUP BY child_uid 

編輯

在我可以寫最快形成完整的查詢。我已從LIKE中刪除%,將子查詢更改爲加入並丟棄*。關於出生日期的條件也被移動。雖然可能有錯誤。

SELECT users.userID, 
    users.categoryGroup, children.childBirthDate, 
    children.childrenID 
FROM 
    (SELECT MIN(childBirthDate) AS childBirthDate, userID 
     FROM children 
     WHERE childBirthDate <= DATE_SUB(CURDATE(), INTERVAL 5 MONTH) 
     AND childBirthDate > DATE_SUB(CURDATE() , INTERVAL 6 MONTH) 
     GROUP BY userID) AS ch1 
    INNER JOIN users ON users.userID = ch1.userID 
    INNER JOIN children ON users.userID = children.userID AND ch1.childBirthDate = children.childBirthDate 
    LEFT JOIN CuponRequests ON CuponRequests.userID = userID AND cuponID = 1 
    WHERE ('Group1' LIKE users.categoryGroup OR 'Group2' LIKE users.categoryGroup) 
    AND CuponRequest.ID IS NULL 
    AND userID = 1 
ORDER BY children.childBirthDate DESC 

長描述

  • 子查詢可能是緩慢的。有時優化器將無法做正確的事情。與ON條款的書寫連接應該更安全。
  • GROUP BY對於優化器而言更爲複雜。這可能有助於在其中寫入更多條件。
  • 使用LIKE '%something%'語句的索引非常困難。 LIKE 'something%'LIKE 'something'要快得多。
  • 有時將*更改爲所需參數的明確列表是個好主意。有時候所有需要的信息都在索引中,不需要直接從表中讀取。在角落案例中可能會有所幫助。
+0

的輸出,這使得它更快!對於查詢(+/-),每個查詢執行時間約爲1.1秒。但它仍然讓我在80秒的加載時間。 –

+0

哇!所有100立方厘米的小計共有1.1秒(約2分鐘) –

+0

您能解釋一下你做了什麼以及爲什麼這會有所幫助嗎? –