I am running a query which takes 2 seconds (much too long!) and returns 16 records:
SELECT * FROM cells WHERE cellbookguid='1' AND cellpageguid='1';
The table named "cells" holds around 20.000 records.
It has 42 columns such as "cellwidth, cellheight, cellcolor, cellbackground, cellpageguid, cellbookguid, etc.".
Because I don't understand why it takes so long, I run EXPLAIN QUERY.
It says:
order: 0
from: 0
detail: TABLE cells WITH INDEX idx_cells_cellbookguid
So obviously it does not use the index idx_cells_cellpageguid.
This must be why it takes so long.
However, this index is present:
SELECT name FROM sqlite_master WHERE type = 'index';"
It does contain "idx_cells_cellpageguid".
I have also used an external SQLite viewer. This index is present on the column.
Why does the SQLite query not use this index?
The definition of idx_cells_cellpageguid is
This is the SQL command:
CREATE INDEX idx_cells_cellpageguid
ON cells
(CellPageGUID COLLATE BINARY)
Thank you!
Related
I'm using the following code to get a user's recovery_token and store it in a variable:
Connect To Database psycopg2 ${DB_NAME}
... ${DB_USER_NAME}
... ${DB_USER_PASSWORD}
... ${DB_HOST}
... ${DB_PORT}
${RECOVERY_TOKEN}= Query select recovery_token FROM public."system_user" where document_number like '57136570514'
Looking at the log, the recovery_token is being saved as follows:
${RECOVERY_TOKEN} = [('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImU3ZGM4MmNjLTliMGQtNDc3OC1hMzM0LWEyNjM4MDU1Mzk1MSIsImlhdCI6MTYyMzE5NjM4NSwiZXhwIjoxNjIzMTk2NDQ1fQ.mdsrQlgaWUol02tZO8dXlL3KEwY6kqwj5T7gfRDYVfU',)]
But I need what is saved in the variable ${RECOVERY_TOKEN} to be just the token, without the special characters [('',)]
${RECOVERY_TOKEN} = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImU3ZGM4MmNjLTliMGQtNDc3OC1hMzM0LWEyNjM4MDU1Mzk1MSIsImlhdCI6MTYyMzE5NjM4NSwiZXhwIjoxNjIzMTk2NDQ1fQ.mdsrQlgaWUol02tZO8dXlL3KEwY6kqwj5T7gfRDYVfU
Is there any way I can remove the special characters?
Thanks in advance!!
The returned value is a list of tuples, a two-dimensional matrix (e.g. a table); if you have queried for 3 columns for example, the inner tuple would have 3 members. And if there were 5 records that match it, the list would have 5 tuples in it.
Thus to get the value you are after, get it from the matrix by its indexes (which are 0-based, e.g. the first element is with index "0"):
${RECOVERY_TOKEN}= Set Variable ${RECOVERY_TOKEN[0][0]}
I understand that SQLite does not have If-Else condition check, and people have been using case statements to get around it. However I want to do a if condition check before executing a certain portion of the script, like the following:
IF (condition = true)
INSERT INTO tableA(A, B)
VALUES (a, b)
....
END
From what I have been trying, case statement doesn't seem to work. Is there any way I can accomplish the above in SQLite?
Thanks for all your help!
You could perhaps use an INSERT SELECT
INSERT INTO table SELECT ...;
The second form of the INSERT statement contains a SELECT statement
instead of a VALUES clause.
A new entry is inserted into the table for
each row of data returned by executing the SELECT statement.
If a
column-list is specified, the number of columns in the result of the
SELECT must be the same as the number of items in the column-list.
Otherwise, if no column-list is specified, the number of columns in
the result of the SELECT must be the same as the number of columns in
the table.
Any SELECT statement, including compound SELECTs and SELECT
statements with ORDER BY and/or LIMIT clauses, may be used in an
INSERT statement of this form.
extract from SQL As Understood By SQLite - INSERT
e.g.
INSERT into xxx
SELECT null as id,
CASE
WHEN filesize < 1024 THEN 'just a little bit'
WHEN filesize >= 1024 THEN 'quite a bit'
END AS othercolumn
FROM filesizes
WHERE filesize < 1024 * 1024
The above will insert rows into table xxx which consists of 2 columns id (rowid alias) and othercolumn according to the results (2 columns id (always set as null) and othercolumn) of the SELECT, which is selecting from the filesizes table where the value of the filesize column is less than 1024 * 1024 (1048576), thus conditionally inserting.
Furthermore, if the filesize is less than 1024 the othercolumn is set to just a little bit, if the filesize is greater than 1023 then the othercolumn is set to quite a bit. So making the conditional insert more complex.
Assuming the filesizes table were :-
The running the above would result in :-
Executing the following code creates a table with two columns and adds 1 million rows. One column is INT and one is TEXT. Then it creates one index per column and one collate nocase index per column. Then it executes three queries.
The first query uses the index t2 as expected.
The second query is the same as the first one, but it adds the ESCAPE clause and doesn't use the index. The presence of unescaped % or _ should prevent the index from being (fully) used, but the presence of the ESCAPE clause itself shouldn't.
Why does the ESCAPE clause prevent the index from being used?
The third query is the same as the first one, but it doesn't use the index. The only difference is that the query uses column col_i instead of col_t which is defined as INT instead of TEXT. Sqlite doesn't prevent me from creating the index, so I would expect for it to be used.
Why isn't the index i2 used?
.timer on
DROP TABLE IF EXISTS tab;
CREATE TABLE tab (col_t TEXT, col_i INT);
INSERT INTO tab (col_i, col_t) WITH RECURSIVE cte (x, y) AS (SELECT hex(randomblob(16)), hex(randomblob(16)) UNION ALL SELECT hex(randomblob(16)), hex(randomblob(16)) FROM cte LIMIT 1000000) SELECT x, y FROM cte;
CREATE INDEX t ON tab (col_t);
CREATE INDEX t2 ON tab (col_t COLLATE nocase);
CREATE INDEX i ON tab (col_i);
CREATE INDEX i2 ON tab (col_i COLLATE nocase);
SELECT * FROM tab WHERE col_t LIKE 'abcabcabc';
SELECT * FROM tab WHERE col_t LIKE 'abcabcabc' ESCAPE '\';
SELECT * FROM tab WHERE col_i LIKE 'abcabcabc';
The documentation documents when the index can be used for LIKE:
The left-hand side … must be the name of an indexed column with TEXT affinity.
The right-hand side … must be … a string literal … that does not begin with a wildcard character.
The ESCAPE clause cannot appear on the LIKE operator.
The built-in functions used to implement LIKE … must not have been overloaded using the sqlite3_create_function() API.
[…]
… the column must indexed using built-in NOCASE collating sequence.
The query optimizer has to prove that using the index cannot change the meaning of the query. These rules implement the proof.
While there exist queries that would work with the index despite violating these rules, it would be necessary to extend the optimizer to be able to prove that they work.
I want to execute a SELECT query on a database table that has 6 key fields, let's assume they are keyA, keyB, ..., keyF.
As input parameters to my ABAP function module I do receive an internal table with exactly that structure of the key fields, each entry in that internal table therefore corresponds to one tuple in the database table.
Thus I simply need to select all tuples from the database table that correspond to the entries in my internal table.
Furthermore, I want to aggregate an amount column in that database table in exactly the same query.
In pseudo SQL the query would look as follows:
SELECT SUM(amount) FROM table WHERE (keyA, keyB, keyC, keyD, keyE, keyF) IN {internal table}.
However, this representation is not possible in ABAP OpenSQL.
Only one column (such as keyA) is allowed to state, not a composite key. Furthermore I can only use 'selection tables' (those with SIGN, OPTIOn, LOW, HIGH) after they keyword IN.
Using FOR ALL ENTRIES seems feasible, however in this case I cannot use SUM since aggregation is not allowed in the same query.
Any suggestions?
For selecting records for each entry of an internal table, normally the for all entries idiom in ABAP Open SQL is your friend. In your case, you have the additional requirement to aggregate a sum. Unfortunately, the result set of a SELECT statement that works with for all entries is not allowed to use aggregate functions. In my eyes, the best way in this case is to compute the sum from the result set in the ABAP layer. The following example works in my system (note in passing: using the new ABAP language features that came with 7.40, you could considerably shorten the whole code).
report zz_ztmp_test.
start-of-selection.
perform test.
* Database table ZTMP_TEST :
* ID - key field - type CHAR10
* VALUE - no key field - type INT4
* Content: 'A' 10, 'B' 20, 'C' 30, 'D' 40, 'E' 50
types: ty_entries type standard table of ztmp_test.
* ---
form test.
data: lv_sum type i,
lt_result type ty_entries,
lt_keys type ty_entries.
perform fill_keys changing lt_keys.
if lt_keys is not initial.
select * into table lt_result
from ztmp_test
for all entries in lt_keys
where id = lt_keys-id.
endif.
perform get_sum using lt_result
changing lv_sum.
write: / lv_sum.
endform.
form fill_keys changing ct_keys type ty_entries.
append :
'A' to ct_keys,
'C' to ct_keys,
'E' to ct_keys.
endform.
form get_sum using it_entries type ty_entries
changing value(ev_sum) type i.
field-symbols: <ls_test> type ztmp_test.
clear ev_sum.
loop at it_entries assigning <ls_test>.
add <ls_test>-value to ev_sum.
endloop.
endform.
I would use FOR ALL ENTRIES to fetch all the related rows, then LOOP round the resulting table and add up the relevant field into a total. If you have ABAP 740 or later, you can use REDUCE operator to avoid having to loop round the table manually:
DATA(total) = REDUCE i( INIT sum = 0
FOR wa IN itab NEXT sum = sum + wa-field ).
One possible approach is simultaneous summarizing inside SELECT loop using statement SELECT...ENDSELECT statement.
Sample with calculating all order lines/quantities for the plant:
TYPES: BEGIN OF ls_collect,
werks TYPE t001w-werks,
menge TYPE ekpo-menge,
END OF ls_collect.
DATA: lt_collect TYPE TABLE OF ls_collect.
SELECT werks UP TO 100 ROWS
FROM t001w
INTO TABLE #DATA(lt_werks).
SELECT werks, menge
FROM ekpo
INTO #DATA(order)
FOR ALL ENTRIES IN #lt_werks
WHERE werks = #lt_werks-werks.
COLLECT order INTO lt_collect.
ENDSELECT.
The sample has no business sense and placed here just for educational purpose.
Another more robust and modern approach is CTE (Common Table Expressions) available since ABAP 751 version. This technique is specially intended among others for total/subtotal tasks:
WITH
+plants AS (
SELECT werks UP TO 100 ROWS
FROM t011w ),
+orders_by_plant AS (
SELECT SUM( menge )
FROM ekpo AS e
INNER JOIN +plants AS m
ON e~werks = m~werks
GROUP BY werks )
SELECT werks, menge
FROM +orders_by_plant
INTO TABLE #DATA(lt_sums)
ORDER BY werks.
cl_demo_output=>display( lt_sums ).
The first table expression +material is your internal table, the second +orders_by_mat quantities totals selected by the above materials and the last query is the final output query.
I have an SQLite table that contains a BLOB I need to do a size/length check on. How do I do that?
According to documentation length(blob) only works on texts and will stop counting after the first NULL. My tests confirmed this. I'm using SQLite 3.4.2.
I haven't had this problem, but you could try length(hex(glob))/2
Update (Aug-2012):
For SQLite 3.7.6 (released April 12, 2011) and later, length(blob_column) works as expected with both text and binary data.
for me length(blob) works just fine, gives the same results like the other.
As an additional answer, a common problem is that sqlite effectively ignores the column type of a table, so if you store a string in a blob column, it becomes a string column for that row. As length works different on strings, it will then only return the number of characters before the final 0 octet. It's easy to store strings in blob columns because you normally have to cast explicitly to insert a blob:
insert into table values ('xxxx'); // string insert
insert into table values(cast('xxxx' as blob)); // blob insert
to get the correct length for values stored as string, you can cast the length argument to blob:
select length(string-value-from-blob-column); // treast blob column as string
select length(cast(blob-column as blob)); // correctly returns blob length
The reason why length(hex(blob-column))/2 works is that hex doesn't stop at internal 0 octets, and the generated hex string doesn't contain 0 octets anymore, so length returns the correct (full) length.
Example of a select query that does this, getting the length of the blob in column myblob, in table mytable, in row 3:
select length(myblob) from mytable where rowid=3;
LENGTH() function in sqlite 3.7.13 on Debian 7 does not work, but LENGTH(HEX())/2 works fine.
# sqlite --version
3.7.13 2012-06-11 02:05:22 f5b5a13f7394dc143aa136f1d4faba6839eaa6dc
# sqlite xxx.db "SELECT docid, LENGTH(doccontent), LENGTH(HEX(doccontent))/2 AS b FROM cr_doc LIMIT 10;"
1|6|77824
2|5|176251
3|5|176251
4|6|39936
5|6|43520
6|494|101447
7|6|41472
8|6|61440
9|6|41984
10|6|41472