SQLite select语句优化建议

时间:2011-05-22 21:28:41

标签: sql sqlite

我有一个带有结构的SQLite表'Details':

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. 由于每个嵌套选择使用Random()函数,因此效率不高。
  3. 有人可以建议另一种方法来选择我需要的数据(使用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
    

5 个答案:

答案 0 :(得分:3)

我在这里和neurino在一起。你还没有说为什么你需要将随机选择的四个名字放在一行中,为什么必须在后端完成。

如果您担心性能,请在客户端生成随机整数(范围> = min(pkcol)和< = max(pkcol)),直到找到四个不同的行(即实体/名称)。生成的id之一可能没有行存在,但只需几毫秒即可找到。采用随机密钥方法,您可以避免订单。即使对于具有数十亿行的表,该方法也可以快速工作。

P.S。 (在发现它是iPhone应用程序后) 您需要一次调用来获取最小和最大ID值(它是PK,因此使用索引)。然后你至少需要再次调用DB(再次,索引辅助)来使用随机生成的PK值获取四个不同的行[其中ID在(a,b,c,d)中]最大调用次数未知;多少将取决于主键序列的密度。我不认为这将是一个过多的I / O,它将比Random()的订单资源密集程度低得多 - 特别是如果表有很多行。您总是可以随机生成8个,12个,16个ID的ID列表,并且如果返回的值超过4个,则客户端只会剔除所需的4行。

P.P.S。通常情况下,数据库连接的实例化是昂贵的,并且您不希望在循环中执行此操作,或者不希望这样做。但是你可以打开一个连接,运行两个或三个有效的选择,每个选择返回几行,然后关闭,如果你完成了手头的任务。

答案 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;

答案 2 :(得分:0)

如何做一个完整的外连接x3,然后简单地选择一个名称不相等的行?

答案 3 :(得分: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之外,我没有看到Random()函数及其生成的缓慢的方法,但是还有其他问题。

答案 4 :(得分: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