2011-09-10 10 views
15

Czy są jakieś instrukcje pętli w SQLite, takie jak FOR .. in .. LOOP czy coś w tym stylu? Mam dwie kolumny StartRange, EndRange i muszę wstawić całą sekwencję w innej tabeli. Więc jeśli StartRange wynosi 1 i EndRange jest 3, konieczne jest utworzenie trzech wstawek o wartości zawierającej 1, 2, 3.Instrukcje pętli SQLite?

+2

obawiam musisz napisać jakiś imperatyw kod do zrobienia to, SQL jest ogólnie językiem deklaratywnym i tylko jego rozszerzenia (takie jak T-SQL, SQL/PL itp.) wspierają struktur kontroli przepływu, takich jak pętle. Jeśli jednak powinno być łatwe, należy wykonać wstawianie w jednym przebiegu za pomocą transakcji SQLite. –

+0

Perl/Python/Ruby-script-to-the-rescue! :) –

+0

Czy należy to przenieść na dba.stackexchange.com? – anddam

Odpowiedz

8

Możesz zrobić tego rodzaju rzeczy w prostym SQL, jeśli masz dodatkową tabelę zawierającą wszystkie liczby całkowite, których potrzebujesz.

Załóżmy swoją StartRange i EndRange zakres od jednego do dziesięciu i masz tabelę tak:

sqlite> select i from ints; 
i 
1 
. 
. 
. 
10 

Tabela ta zawiera po prostu wszystkich możliwych liczb całkowitych, które trzeba (to znaczy jeden do dziesięciu).

Następnie, jeśli masz również to:

sqlite> create table t (startrange int not null, endrange int not null); 
sqlite> insert into t values(1, 3); 
sqlite> create table target (i int not null); 

można zrobić wkładek do target z złączenia:

insert into target (i) 
select ints.i 
from ints join t on (ints.i >= t.startrange and ints.i <= t.endrange) 

Rezultat jest taki:

sqlite> select * from target; 
i 
1 
2 
3 

Oczywiście Twój prawdziwy t miałby więcej wierszy, więc potrzebujesz klauzuli WHERE do ograniczenia tego wiersza z t, którego szukasz.

Podobne rzeczy często robimy z datami (spójrz na "tabele kalendarza").

Więc jeśli zakresy są małe (dla niektórych definicji małej), a następnie wygenerować tabelę ints raz, dodać indeks do niego i użyj powyższej techniki zrobić wszystkie wkładek bezpośrednio w bazie danych. Inne bazy danych mają własne sposoby (takie jak PostgreSQL's generate_series) do robienia tego typu rzeczy bez potrzeby wyraźnej tabeli ints, ale SQLite jest (celowo) ograniczony.

SQL jest ogólnie oparty na ustawieniach, więc pętle nie są naturalne. To, co naturalne, to budowanie odpowiednich zestawów poprzez opisywanie tego, czego potrzebujesz. OTOH, czasami nienaturalne działania są konieczne i rozsądne.

Nie wiem, czy to ma sens dla twojej aplikacji, po prostu pomyślałem, że pokażę, jak można to zrobić. Jeśli takie podejście nie ma sensu w twoim przypadku, możesz wygenerować kilka instrukcji INSERT poza bazą danych.

+0

Tabele SQLite mają numery wierszy, które mogą być używane w zapytaniach. – reinierpost

+0

@reinierpost Czy odnosisz się do 'rowid'? –

+0

Tak. (i z twoim imieniem, to jest * nadal * za krótko) – reinierpost

14

Możesz tworzyć pętle w SQL z rekurencyjnymi wyzwalaczami.Korzystanie mu jest zbyt krótki „s schematu

sqlite> create table t (startrange int not null, endrange int not null); 
sqlite> insert into t values(1, 3); 
sqlite> create table target (i int not null); 

musimy umożliwić rekurencyjnych wyzwalaczy w SQLite:

sqlite> PRAGMA recursive_triggers = on; 

Zrób tymczasowe spust do pętli aż do końca zakresu:

sqlite> create temp trigger ttrig 
    ...> before insert on target 
    ...> when new.i < (select t.endrange from t) begin 
    ...> insert into target values (new.i + 1); 
    ...> end; 

kick it off:

sqlite> insert into target values ((select t.startrange from t)); 
sqlite> select * from target; 
3 
2 
1 
sqlite> 
+2

Oto czarna magia, której potrzebowałem. Dzięki stary! –

2

Najwyraźniej konstruktem pętli w SQLite jest klauzula WITH RECURSIVE. Ten odnośnik do dokumentacji zawiera przykładowy kod od liczby dziesiętnej do dziesięciu, ploter z zestawu Mandelbrota i narzędzie do rozwiązywania zagadek sudoku, wszystkie w czystym SQL. Oto kwerendy SQLite, który oblicza ciągu Fibonacciego, aby dać Ci poczuć do niego:

sqlite> WITH RECURSIVE 
    ...> fibo (curr, next) 
    ...> AS 
    ...> (SELECT 1,1 
    ...>  UNION ALL 
    ...>  SELECT next, curr+next FROM fibo 
    ...>  LIMIT 100) 
    ...> SELECT group_concat(curr) FROM fibo; 
1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,... 

A oto Sieve of Eratosthenes:

begin transaction; 

drop table if exists naturals; 
create table naturals 
(n integer unique primary key asc, 
    isprime bool, 
    factor integer); 

with recursive 
    nn (n) 
as (
    select 2 
    union all 
    select n+1 as newn from nn 
    where newn < 1e4 
) 
insert into naturals 
select n, 1, null from nn; 

insert or replace into naturals 
    with recursive 
    product (prime,composite) 
    as (
    select n, n*n as sqr 
     from naturals 
     where sqr <= (select max(n) from naturals) 
    union all 
    select prime, composite+prime as prod 
    from 
     product 
    where 
     prod <= (select max(n) from naturals) 
) 
select n, 0, prime 
from product join naturals 
    on (product.composite = naturals.n) 
; 
commit;