2011-05-22 90 views
3

我有一個SQLite表「詳細資料」與結構:SQLite的select語句的優化建議

ID Name Category 
--------------------- 
1 Matt 0 
2 Shervin 0 
3 Bob  0 
4 Lee  0 
5 Rick 0 
6 Suraya 0 
7 Susan 0 
8 Adam 0 
9 Jon  1 
10 Lorna 1 
... and so on ....... 

我想隨機選擇一排,然後從三個不同行的三個名字(同樣優選隨機)。我想這一切都從一個SQLite語句返回。例如。

ID Name Category Name1 Name2 Name 3 
---------------------------------------- 
3 Bob 0   Matt Lee Susan 

我在這一嘗試可以看到下面,但它有兩個問題:

  1. 三個額外的名稱不一定總是不同的 - 我似乎無法排除已經名稱之前選擇,因爲變量b/c/d不在他們自己的COALESCE函數的範圍之內。
  2. 由於每個嵌套select使用Random()函數,因此效率不高。

任何人都可以提出另一種方法來選擇我需要的數據(使用SQLite數據庫)?任何幫助/建議都是值得歡迎的 - 希望明確我想達到的目標,隨時要求澄清。

我當前的嘗試:

SELECT a.Id, 
     a.Name, 
     a.Category, 
     COALESCE((SELECT b.Name 
        FROM Details b 
        WHERE b.Id NOT IN (a.Id) 
        AND b.Category IN (0) 
       ORDER BY Random() 
        LIMIT 1),'') as "Name1", 
     COALESCE((SELECT c.Name 
        FROM Details c 
        WHERE c.Id NOT IN (a.Id) 
        AND c.Category IN (0) 
       ORDER BY Random() 
        LIMIT 1),'') as "Name2", 
     COALESCE((SELECT d.Name 
        FROM Details d 
        WHERE d.Id NOT IN (a.Id) 
        AND d.Category IN (0) 
       ORDER BY Random() 
        LIMIT 1),'') as "Name3" 
    FROM Details a 
    AND a.Category IN (0) 
ORDER BY Random() 
    LIMIT 1 
+0

我看不到這樣做_shuffle_在SQL中,而不是在代碼中任何優勢將要處理的數據(如果有的話當然) – neurino 2011-05-22 21:41:45

+0

@neurino:如果你在SQL中這樣做的話,你可能會比傳遞整個列表中的客戶端項目更少的數據傳輸給客戶端。 – 2011-05-23 08:33:30

+0

@neurino:我在iPhone應用程序中使用此代碼。所以我的思考過程是儘量減少傳輸的數據量和對SQLite數據庫的調用次數。 – MattStacey 2011-05-23 09:32:11

回答

3

我與neurino在這裏。你沒有說過爲什麼你需要將隨機選擇的四個名字放在一行中,爲什麼必須在後端完成。

如果您關注性能,請在客戶端生成隨機整數(範圍> = min(pkcol)和< = max(pkcol)),直到找到四個不同的行(即實體/名稱)。一個生成的id可能不存在任何行,但只需要幾毫秒即可找出結果。採用這種隨機密鑰方法,您可以避免通過訂單。即使對於擁有數十億行的表格,該方法也可以快速工作。

P.S. (發現它是iPhone應用程序後) 您需要一次調用才能獲取最小和最大ID值(這是使用索引的PK)。然後,至少需要另一個對數據庫的調用(同樣,索引輔助),以使用隨機生成的PK值[其中(a,b,c,d)中的ID]獲取四個不同的行。 ;有多少將取決於您的主鍵序列的密度。我不相信這會是一個過多的I/O,並且它比Random()的命令佔用的資源要少得多 - 尤其是如果表有很多行時。您隨時可以隨機生成8,12,16個ID的ID列表,並讓您的客戶端僅剔除需要返回4個以上的4行。

P.P.S.通常情況下,數據庫連接的實例化非常昂貴,並且您不希望在循環中執行該操作,或者更不經常執行該操作。但是你可以打開一個連接,運行兩個或三個有效的選擇,每個返回幾行,然後關閉,如果你完成了手頭的任務。

+0

您好,我正在iPhone應用程序中使用SQLite語句,並認爲過程(也許不正確???)嘗試並儘量減少數據庫和iPhone應用程序之間傳輸的數據。你的想法是一個有趣的方法,但我認爲我已經在某處讀過蘋果指導方針是儘可能減少對數據庫的調用 - 事後我應該提到這是針對iPhone應用程序的問題。 – MattStacey 2011-05-23 09:40:05

+0

參見附錄在我的答案中。 – Tim 2011-05-24 11:57:46

+0

非常感謝您的意見Tim提供的幫助使我從另一個角度看待這個問題。想想我的最終解決方案將來自您和Andriy的評論 – MattStacey 2011-05-24 23:23:52

0

如何做一個完全外部連接X3,然後簡單地隨機選擇一排,其中名稱不相同?

0

您也可以通過嵌套查詢來獲得想要的名稱作爲返回值。你基本上先得到第四個值,然後是第三個值,依此類推。一直確保它們不匹配。我應該通過Id字段並檢查Id是否與名稱不衝突,但這種方式意味着唯一的名稱。

SELECT Id 
     ,Name 
     ,Category 
     ,bName 
     ,cName 
     ,dName 
FROM Details, 
    (
     SELECT Name AS bName, cName, dName 
     FROM Details, 
      (
       SELECT Name AS cName, dName 
       FROM Details, 
        (
         SELECT Name AS dName 
         FROM Details 
         WHERE Category IN (0) 
         ORDER BY Random() 
         LIMIT 1 
        ) td 
       WHERE Name <> dName 
       AND Category IN (0) 
       ORDER BY Random() 
       LIMIT 1 
      ) tc 
     WHERE Name <> dName 
     AND Name <> cName 
     AND Category IN (0) 
     ORDER BY Random() 
     LIMIT 1 
    ) tb 
WHERE Name <> dName 
AND Name <> cName 
AND Name <> bName 
AND Category IN (0) 
ORDER BY Random() 
LIMIT 1; 

我沒有看到周圍的隨機()函數的一種方法,它產生比產生隨機ID在代碼之外的緩慢,但還有其他問題。

0

通過random()語句優化訂單有兩種主要方法。

首先是完全刪除整個表格的排序,但它不適用於所有平臺:limit 1 offset random(),而不是order by random() limit 1

另一個適用於所有平臺,但要求您的主鍵相當密集(一個自動遞增的整數,如果有任何刪除操作確保它們不變)。預取從隨機起點開始的一組較小的ID,並在子查詢中使用它們:

select * 
from (select * 
     from tbl 
     where id between :x and :x + 20 
    ) 
order by random() 
limit 1 
1

多語句的解決方案,它使用一個臨時表:

CREATE TEMP TABLE names 
AS 
SELECT 
    Id, 
    Name, 
    Category 
FROM Details 
WHERE Category IN (0) 
ORDER BY Random() 
LIMIT 4; 

SELECT 
    MAX(CASE rowid WHEN 1 THEN Id END) AS Id, 
    MAX(CASE rowid WHEN 1 THEN Name END) AS Name, 
    MAX(CASE rowid WHEN 1 THEN Category END) AS Id, 
    MAX(CASE rowid WHEN 2 THEN Name END) AS Name1, 
    MAX(CASE rowid WHEN 3 THEN Name END) AS Name2, 
    MAX(CASE rowid WHEN 4 THEN Name END) AS Name3 
FROM names; 

DROP TABLE names; 
+0

謝謝Andriy,看起來可能正好符合我的需求。當我有機會(可能是週末 - 在辦公室忙碌的一週),我會試一試。 – MattStacey 2011-05-24 23:40:40