Совет по оптимизации оператора SQLite select - PullRequest
3 голосов
/ 23 мая 2011

У меня есть таблица 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 (), это не очень эффективно.

Кто-нибудь может предложить другой способ выбора нужных мне данных (используя базу данных 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 ]

3 голосов
/ 23 мая 2011

Я здесь с неврино. Вы не сказали, почему вам нужно поместить произвольно выбранные четыре имени в один ряд, и почему это должно быть сделано на заднем плане.

Если вы беспокоитесь о производительности, генерируйте случайные целые числа в вашем клиенте (диапазон> = min (pkcol) и <= max (pkcol)), пока не найдете четыре отдельные строки (то есть сущности / имена). Существует вероятность, что с одним из сгенерированных идентификаторов не существует ни одной строки, но на это уходит всего несколько миллисекунд. Принимая этот случайный ключ, вы можете избежать заказа. Подход будет работать быстро даже для таблиц с миллиардами строк. </p>

P.S. (Узнав, что это приложение для iPhone) Вам нужен один вызов, чтобы получить минимальное и максимальное значения идентификатора (это PK, который использует индекс). Затем вам потребуется как минимум еще один вызов в БД (опять же, с помощью индекса), чтобы получить четыре разных строки, используя ваши случайно сгенерированные значения PK [где ID в (a, b, c, d)]. Максимальное количество вызовов неизвестно. ; сколько будет зависеть от плотности вашей первичной последовательности ключей. Я не верю, что это будет чрезмерное количество операций ввода-вывода, и оно будет значительно менее ресурсоемким, чем порядок в Random () - особенно если в таблице много строк. Вы всегда можете сгенерировать список идентификаторов из 8, 12, 16 идентификаторов в случайном порядке, и ваш клиент будет отбирать только 4 строки, необходимые, если возвращается более 4.

P.P.S. Обычно это дорогостоящее создание соединения с базой данных, и вы не хотите делать это в цикле или чаще, чем вам нужно. Но вы можете открыть соединение, выполнить два или три эффективных выбора, которые возвращают по несколько строк в каждой, а затем закрыть, если вы закончили с поставленной задачей.

1 голос
/ 24 мая 2011

Решение с несколькими операторами, которое использует временную таблицу:

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 голосов
/ 23 мая 2011

Вы также можете достичь желаемого, вложив запросы, чтобы имена имели возвращаемое значение. По сути, сначала вы получаете четвертое значение, а затем третье и так далее. Все время гарантируя, что они не совпадают. Я должен был пройти через поле 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;

Я не вижу пути обхода функции Random () и медленности, которую она генерирует, кроме генерации случайных идентификаторов в коде, но у этого есть другие проблемы.

0 голосов
/ 23 мая 2011

Существует два основных метода оптимизации порядка с помощью оператора random ().

Первый - полностью удалить сортировку всего шага таблицы, но она работает не на всех платформах: limit 1 offset random(), а не order by random() limit 1.

Другая работает на всех платформах, но требует, чтобы ваши первичные ключи были достаточно плотными (автоматическое увеличение целого числа без каких-либо исключений, если их удаляет, гарантирует, что они есть).Предварительно получить меньший набор идентификаторов, начиная со случайной начальной точки, и использовать их в подзапросе:

select *
from (select *
      from tbl
      where id between :x and :x + 20
      )
order by random()
limit 1
0 голосов
/ 23 мая 2011

Как насчет выполнения полного внешнего соединения x3, а затем простого случайного выбора строки, в которой имена не равны?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...