SQLite сравнивает массив идентификаторов с теми, что хранятся в базе данных в виде строки - PullRequest
0 голосов
/ 06 января 2019

Я использую SQLite в качестве базы данных. В таблице у меня есть столбец с именем projectIds, в котором хранится разделенная запятыми строка набора идентификаторов (целых). Я хочу создать способ поиска в этой таблице, чтобы он возвращал все строки, в которых хранятся конкретные идентификаторы проекта.

Возможно ли, как я его храню? Есть ли какой-нибудь способ взять массив projectIds и создать запрос, который проверит столбец projectIds, если какой-либо из этих идентификаторов существует?

, например

Return me all rows in the table that have one of these projectIds in it: [1,2,3,4]

Table:
row1 "1,3,4"
row2 "1"
row3 "5,6"

I should be returned rows 1 and 2.

Есть ли надежный способ взять мой массив и сравнить его со столбцом projectIds, который представляет собой строку идентификаторов?

Ответы [ 4 ]

0 голосов
/ 19 января 2019

Возможно ли, как я его храню?

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

Есть ли какой-нибудь способ взять массив идентификаторов проектов и создать запрос, который проверит столбец projectIds, если какой-либо из этих идентификаторов существует?

Предполагая, что ваша проблема не так проста, как, например, несколько projectid считают, что ваша таблица может стать более похожей (может потребоваться миллион LIKE): -

enter image description here

Тогда можно увидеть, что использование WHERE Like ......... может легко стать длинным и, возможно, более простым для кодирования вне SQL.

Однако можно использовать рекурсивное общее табличное выражение (CTE), которое генерирует CTE ( s в приведенном ниже примере, который имеет rowid (не должно быть таблицей БЕЗ ROWID) Кластерные индексы и оптимизация БЕЗ ПУТИ ))

Следующая таблица подойдет для примера (включая DROP и CREATE таблицы (с именем table_a )): -

-- Drop and create the table
DROP TABLE IF EXISTS table_a;
CREATE TABLE table_a (projectid_list TEXT);

-- Add some data for testing
INSERT INTO table_a VALUES
    ('1,2,3'),  -- id = 1
    ('2,3'),    -- id = 2
    ('4,5,6,7'), -- id = 3
    ('3,2,1'), -- id = 4
    ('1,3,5,7,9,11'), -- id = 5
    ('99,1001,30000,100000000') -- id = 6
;

-- Display the table
SELECT * FROM table_a;

-- The RECURSIVE CTE
WITH RECURSIVE s(id,pvalue,remainder) AS 
    (
            SELECT rowid AS id,'', projectid_list || ',' FROM table_a WHERE id
          UNION ALL
                SELECT id, 
                        substr(remainder,0,instr(remainder,',')), 
                        substr(remainder,instr(remainder,',') +1)
                        FROM s
                        WHERE remainder <> '' -- This ends the recursion
                        LIMIT 1000 -- Just in case end the recursion after 1000 

    )
SELECT id FROM s WHERE CAST(pvalue AS INTEGER) IN (1,2,3,11) GROUP BY id; 
  • Обратите внимание, что существует LIMIT 1000, он был включен для защиты во время тестирования и может быть удален

Приведенные выше результаты приводят к (приведенная выше таблица, содержащая список projectid_list на строку) и список rowid ( id , если это псевдоним столбца rowid ) согласно критерии поиска 1,2,3,11 в примере: -

enter image description here

Сам CTE выглядит следующим образом после постепенного получения идентификаторов проекта из списка в виде pvalue с id в качестве соответствующего rowid , остаток = список идентификаторов проекта, который используется для следующей итерации: -

enter image description here

0 голосов
/ 07 января 2019

Шаблон поиска LIKE '%,1,%' не будет соответствовать значению типа '1,3,4'.
Поэтому вы должны сравнить его с такими значениями, как ',1,3,4,'.
Для этого необходимо добавить , в начале и в конце значения столбца projectIds после удаления (если есть) всех пробелов с помощью REPLACE():

SELECT * 
FROM tablename 
WHERE 
  ',' || REPLACE(projectIds, ' ', '') || ',' LIKE '%,1,%'
  OR
  ',' || REPLACE(projectIds, ' ', '') || ',' LIKE '%,2,%'
  OR
  ',' || REPLACE(projectIds, ' ', '') || ',' LIKE '%,3,%'
  OR
  ',' || REPLACE(projectIds, ' ', '') || ',' LIKE '%,4,%'

Это код, который соответствует вашей текущей структуре таблицы.
. Конечно, вы должны изменить эту структуру на что-то более гибкое.

0 голосов
/ 07 января 2019

Один из вариантов - использовать пользовательскую функцию (UDF), которая знает, как работать со значениями, разделенными запятыми. В PDO вы можете использовать PDO::sqliteCreateFunction(), чтобы сделать функцию PHP доступной в операторах SQL.

Далее определяется UDF, который вычисляет пересечение двух строк значений через запятую и использует его для фильтрации строк в запросе.

function csv_intersection($vals, $search) {
    $vals_array   = explode(',', $vals);
    $search_array = explode(',', $search);
    $intersection = array_intersect($vals_array, $search_array);
    return implode(',', $intersection);
}

// $db is a PDO instance
$db->sqliteCreateFunction('intersection', 'csv_intersection', 2);

$stmt = $db->prepare('SELECT name, vals FROM example WHERE intersection(vals, ?)');
$stmt->execute(array('1,2,3'));
foreach ($stmt as $row) {
    var_dump($row);
}

» См. Этот пример, работающий в режиме онлайн

0 голосов
/ 06 января 2019

Самый простой способ - изменить схему базы данных, чтобы у вас была отдельная таблица, содержащая строки типа (refid, projectId), где refid ссылается на вашу существующую таблицу.

Вы также можете попробовать использовать оператор LIKE, поэтому запрос для [1,2,3] становится:

SELECT * FROM tbl WHERE projectIds LIKE '%,1,%' OR projectIds LIKE '%,2,%' OR projectIds LIKE '%,3,%'

Но это не охватило бы первый или последний элемент projectIds, поэтому лучшим способом без изменения схемы было бы прочитать всю таблицу и проанализировать ее программно.

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