2009-04-10 30 views
5

Tôi đã sử dụng SQL trong nhiều năm nay nhưng hiếm khi có gì thêm vào đó chèn đơn giản và chọn v.v ... vì vậy tôi không có chuyên gia SQL. Tôi tự hỏi nếu tôi có thể nhận được một số trợ giúp trong việc tối ưu hóa một câu lệnh SQL phức tạp hơn mà tôi đang thực hiện trên SQLite, từ PHP thông qua PDO.Tối ưu hóa đa lựa chọn chèn SQLite

Tuyên bố có vẻ hoạt động chính xác, dường như mất nhiều thời gian hơn tôi dự kiến ​​(hoặc có lẽ tôi chỉ mong đợi quá nhiều).

Đây là SQL:

INSERT OR IGNORE INTO MailQueue(SubscriberID, TemplateID) 
    SELECT Subscribers.ID, '1' AS TemplateID 
    FROM Subscribers 
    INNER JOIN SubscriberGroups ON Subscribers.ID=SubscriberGroups.SubscriberID 
    WHERE SubscriberGroups.GroupID IN ('1', '2', '3') 
    AND Subscribers.ID NOT IN 
     ( 
     SELECT Subscribers.ID FROM Subscribers 
     INNER JOIN SubscriberGroups ON Subscribers.ID=SubscriberGroups.SubscriberID 
     WHERE SubscriberGroups.GroupID IN ('4', '5', '6') 
     ); 

Những gì tôi đã có được một danh sách các thuê bao, trong một hoặc nhiều nhóm. Tôi muốn thêm người đăng ký vào hàng đợi thư, chọn những người thuộc một hoặc nhiều nhóm (1,2,3) nhưng loại trừ những người đăng ký cũng thuộc một nhóm nhóm khác (4,5,6).

Thứ nhất, SQL ở trên là điển hình về cách thực hiện điều này?

Thứ hai, tôi nên làm gì để làm công việc này hiệu quả nhất có thể?

Hiện tại, mất khoảng 30 giây để có được khoảng 5000 hồ sơ người đăng ký (và một số ít nhóm) trên LAMP spec avg.

Vào cuối ngày, hiệu suất không quan trọng, nhưng tôi muốn hiểu nội dung này tốt hơn để mọi thông tin chi tiết được đánh giá cao.

Brad

Trả lời

6

Cơ hội là các lần gia nhập thêm đang giết bạn. Điều gì nếu bạn làm:

SELECT Subscribers.ID, '1' AS TemplateID 
FROM Subscribers 
WHERE EXISTS(SELECT * 
       FROM SubscriberGroups 
       WHERE Subscribers.ID=SubscriberGroups.SubscriberID 
           AND SubscriberGroups.GroupID IN ('1', '2', '3')) 

    AND NOT EXISTS(SELECT * 
        FROM SubscriberGroups 
        WHERE Subscribers.ID=SubscriberGroups.SubscriberID 
        AND SubscriberGroups.GroupID IN ('4', '5', '6') 
    ); 

Bạn cũng sẽ muốn chắc chắn rằng bạn có một chỉ mục trên SubscriberGroups (subscriberID, groupId)

tôi đoán là thuê bao đã có một chỉ mục trên ID, phải không?

EDIT: Một tùy chọn khác, có thể hoặc không thể nhanh hơn. Nhìn vào kế hoạch truy vấn của mỗi để xem ...

Cái này có thể là một chỉ số duy nhất quét mà thể được nhanh hơn so với hai chỉ số tìm kiếm, nhưng nó phụ thuộc vào ưu SQLite của ...

SELECT Subscribers.ID, '1' AS TemplateID 
FROM Subscribers 
INNER JOIN(SELECT SUM(CASE WHEN GroupID IN('1', '2', '3') THEN 1 ELSE 0 END) AS inGroup, 
        SUM(CASE WHEN GroupID IN('4', '5', '6') THEN 1 ELSE 0 END) AS outGroup, 
        SubscriberID 
          FROM SubscriberGroups 
         WHERE SubscriberGroups.GroupID IN ('1', '2', '3', '4', '5', '6') 
     ) SubscriberGroups 
     ON Subscribers.ID=SubscriberGroups.SubscriberID 
     AND inGroup > 0 
     AND outGroup = 0 
+0

Cảm ơn Matt, điều đó thật tuyệt. Giải pháp đầu tiên của bạn đã nhận được nó từ 30 giây xuống còn khoảng 5 hoặc 6 là đủ tốt. Tôi đã không thử tùy chọn thứ hai vì tôi không thực sự hiểu nó, nhưng tôi sẽ giữ nó trong tâm trí nếu nó trở thành một vấn đề. Cảm ơn lần nữa –

3

Một cách khác để viết SQL mà có thể được nhanh hơn (tôi không có SQLite trên đó để kiểm tra):

SELECT 
    S.ID, 
    '1' AS TemplateID  -- Is this really a string? Does it need to be? 
FROM 
    Subscribers S 
LEFT OUTER JOIN SubscriberGroups SG ON 
    SG.SubscriberID = S.ID 
WHERE 
    SG.SubscriberID IS NULL AND 
    EXISTS 
    (
      SELECT 
       * 
      FROM 
       SubscriberGroups SG2 
      WHERE 
       SG2.SubscriberID = S.ID AND 
       SG2.GroupID IN ('1', '2', '3') -- Again, really strings? 
    ) 

phương pháp của Matt cũng nên làm việc tốt. Tất cả chỉ phụ thuộc vào cách SQLite quyết định tạo ra các kế hoạch truy vấn.

Ngoài ra, vui lòng lưu ý nhận xét của tôi. Nếu chúng thực sự được định nghĩa là kiểu dữ liệu INT trong cơ sở dữ liệu của bạn, sẽ có một số xử lý bổ sung để chuyển đổi giữa hai loại dữ liệu khác nhau. Nếu chúng là các chuỗi trong cơ sở dữ liệu, có lý do nào cho điều đó không? Bạn có các giá trị không phải số trong các cột đó không?

+0

Cảm ơn Tom, bạn đã đúng với ID được trích dẫn ... không chắc tại sao tôi lại có chúng ở đó.Tôi đã không thử đề nghị của bạn bởi vì matt của dường như làm việc tốt và dường như của bạn bỏ lỡ phần loại trừ (4,5,6). Dù sao cũng cảm ơn bạn! –

+0

Trên thực tế nhìn vào điều này chặt chẽ hơn tôi nhận ra một trong những cột ID không được khai báo là INTEGER đó là lý do tại sao tôi cần các dấu ngoặc kép. SQLite là không thực sự cầu kỳ về các loại đó là lý do tại sao tôi bị mất nó. Thay đổi thành số nguyên và xóa dấu ngoặc kép, nó hiện chạy trong khoảng 1/2 giây. Cảm ơn! –

+0

Tôi nên chăm sóc các loại trừ. Bạn đã thử nó chưa? Đó là những gì toàn bộ LEFT JOIN là về. Kiểm tra cột NOT NULL từ bảng đã nối trong mệnh đề WHERE và nếu đó là NULL, bạn biết rằng không có kết quả nào tồn tại. –