使用SQLite中的单个查找基于内容更新文本列

时间:2016-04-25 08:20:13

标签: android ios sql sqlite sql-update

在我的iOS和Android项目中,我有一个看起来像这样的SQLite表:

id    words
-----------
1     apple, banana, orange, peach, strawberry
2     car, plane, orange
3     sheep, car, plane, horse, cow
.     ...
.
.

words列是TEXT列,其中包含以逗号分隔的单词列表。

我想通过在列表的前面添加一个单词来更新特定行的单词列表。该列表不应超过5个项目,因此如有必要,我也会删除最后一个单词。

例如,如果我使用row id 1更新cherry,那么我会得到

cherry, apple, banana, orange, peach

或者如果我在row id 2上进行相同的更新,那么我会得到

cherry, car, plane, orange

我的问题

我知道我可以进行查询以获取行,处理文本,然后更新行。但是,这需要两个表查找,一个用于查询,另一个用于更新。这可能与单个更新查找有关吗?

我知道有replace() function,但我不会在这里替换任何东西。我也不仅仅是incrementing an integer。我在SQLite core functions中没有看到任何明显的东西。

1 个答案:

答案 0 :(得分:3)

Correct solution

The words column is a TEXT column that holds a comma deliminated list of words.

1 NF. Column contains atomic data. Normalize schema to get clean code and better performance.

Workaround solution:

SQLite does not have built-in reverse function, that is why it is a bit ugly:

CREATE TABLE mytable(id    INTEGER  NOT NULL, words TEXT );

INSERT INTO mytable(id,words) VALUES (1,'apple, banana, orange, peach, strawberry');
INSERT INTO mytable(id,words) VALUES (2,'car, plane, orange');
INSERT INTO mytable(id,words) VALUES (3,'sheep, car, plane, horse, cow');
INSERT INTO mytable(id,words) VALUES (4,'sheep, cherry, plane, horse, cow');

UPDATE mytable
SET words = CASE 
            WHEN (LENGTH(words) - LENGTH(REPLACE(words, ',', ''))) < 4 
            THEN 'cherry, ' || words
            ELSE  SUBSTR('cherry, ' || words, 1,
                        LENGTH(words) + LENGTH('cherry, ') -
                        LENGTH(SUBSTR(SUBSTR(
                      SUBSTR( SUBSTR(words, INSTR(words,',')+1), INSTR(SUBSTR(words, INSTR(words,',')+1), ',')+1),
                      INSTR(SUBSTR( SUBSTR(words, INSTR(words,',')+1), INSTR(SUBSTR(words, INSTR(words,',')+1), ',')+1), ',') + 1),
                      INSTR(SUBSTR(
                      SUBSTR( SUBSTR(words, INSTR(words,',')+1), INSTR(SUBSTR(words, INSTR(words,',')+1), ',')+1),
                      INSTR(SUBSTR( SUBSTR(words, INSTR(words,',')+1), INSTR(SUBSTR(words, INSTR(words,',')+1), ',')+1), ',') + 1),',')+1)) -1
                      )
            END
WHERE words NOT LIKE '%cherry%';

SELECT * FROM mytable;

SqlFiddleDemo

To make it more general you need to change cherry with your value.

Output:

╔════╦══════════════════════════════════════╗
║ id ║                words                 ║
╠════╬══════════════════════════════════════╣
║  1 ║ cherry, apple, banana, orange, peach ║
║  2 ║ cherry, car, plane, orange           ║
║  3 ║ cherry, sheep, car, plane, horse     ║
║  4 ║ sheep, cherry, plane, horse, cow     ║
╚════╩══════════════════════════════════════╝

How it works:

  • UPDATE ... WHERE words NOT LIKE '%cherry%'; do not update rows that have cherry already
  • SET words = CASE WHEN (LENGTH(words) - LENGTH(REPLACE(words, ',', ''))) < 4 if number of delimeters (commas) is lower than 4 just concatenate value to string
  • if number of commas is 4 that means that it has 5 values. Add desired string at the beginning, then use SUBSTRING from 1 to last comma

SQL Server version for comparison:

DECLARE @val VARCHAR(100) = 'cherry';

UPDATE mytable
SET words = CASE 
            WHEN LEN(words)-LEN(REPLACE(words, ',', '')) < 4 THEN @val + ', ' + words
            ELSE LEFT(@val + ', ' + words,
                      LEN(@val + ', ' + words) - CHARINDEX(',', REVERSE(words)))
            END
WHERE words NOT LIKE '%'+ @val  +'%';

SELECT * FROM mytable;

LiveDemo