Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 29 days ago.
Improve this question
Given a text column in SQLite, how can I reverse its values?
For example, given this table:
CREATE TABLE "testwords" ("word" text);
INSERT INTO "testwords"("word") VALUES ('Hello'), ('world!'), ('foo bar baz');
which SELECT statements give 'olleH', '!dlrow', 'zab rab oof'?
You can do so with a recursive CTE (which is a bit involved for such a simple task):
-- Create and populate example table:
CREATE TABLE "testwords" ("word" text);
INSERT INTO "testwords"("word") VALUES ('Hello'), ('world!'), ('foo bar baz');
-- CTE to select the reversed characters from the "testwords" table:
WITH RECURSIVE "reverse" ("stringno", "string", "charno", "char") AS (
SELECT -- start at the last character:
_rowid_
, "word"
, length("word")
, substr("word", -1, 1)
FROM "testwords"
UNION ALL
SELECT -- recurse: move one letter to the left
"stringno"
, "string"
, "charno" - 1
, substr("string", "charno" - 1, 1)
FROM "reverse"
WHERE "charno" > 1 -- stop condition
)
SELECT
"string"
, group_concat("char", '') AS "reversed"
FROM "reverse"
GROUP BY "stringno";
The recursive CTE selects each character of each string (from right to left), and
stringno is a number suitable to group the characters of each string
string is the string that is being reversed
charno is the position of this character (and counts from length() down to 1)
char is the character itself
Finally, group_concat() is used to re-assemble the individual characters, giving the desired result:
| Hello | olleH |
| world! | !dlrow |
| foo bar baz | zab rab oof |
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I write a query which returns result like this :
but I should reshape result like below :
in fact, what I want is to track date and doc_no and qty for specific act_desc_, Can someone help me to rewrite query reshape in PL SQL? I would appreciate it if somebody can help me.
Since you've posted an image, i'll give you an example of how you can do it using some quick mock data, with SQL:
select ACT_DESC, MAX(EXCAVATION_QTY) as EXCAVATION_QTY, MAX(FORM_WORK_COMPLETION_QTY) as FORM_WORK_COMPLETION_QTY
from (
select QTY, ACT_DESC, TASK,
CASE WHEN TASK = 'EXCAVATION' THEN QTY ELSE NULL END as EXCAVATION_QTY,
CASE WHEN TASK = 'Form Work Completion' THEN QTY ELSE NULL END as FORM_WORK_COMPLETION_QTY
FROM
(
select 1 as QTY, 'a' as ACT_DESC, 'Excavation' as TASK
UNION ALL
select 2 as QTY, 'a' as ACT_DESC, 'Form Work Completion' as TASK
UNION ALL
select 11 as QTY, 'b' as ACT_DESC, 'Excavation' as TASK
UNION ALL
select 22 as QTY, 'b' as ACT_DESC, 'Form Work Completion' as TASK
) as t
) t1
GROUP BY ACT_DESC
It takes:
and converts it into:
/*
There are numerous complex queries that can be used, but here is a quick
and simple method that creates a base table first, then populates it after.
*/
--Step 1: Create empty base table with columns of the correct data type
CREATE TABLE BASE_TABLE AS
(
SELECT
ACT_DESC_,
QTY AS EXCAVATION_QTY,
APP_DATE AS EXCAVATION_APP_DATE,
DOC_NO AS EXCAVATION_DOC_NO,
QTY AS FORM_WORK_COMP_QTY,
APP_DATE AS FORM_WORK_COMP_APP_DATE,
DOC_NO AS FORM_WORK_COMP_DOC_NO,
FROM
SOURCE_TABLE --this is the original table, or the table from the first image
WHERE 0=1 --this clause condition is used to create an empty table
);
--Step 2: Insert into base table the unique ACT_DESC_ values
INSERT INTO BASE_TABLE
(
SELECT DISTINCT
ACT_DESC_
FROM
SOURCE_TABLE
);
--Using Merge statements
--Step 3: Update base table based on TASK_ = 'Excavation'
MERGE INTO BASE_TABLE TGT --Alias TGT short for TarGeT table
USING
(
SELECT * FROM SOURCE_TABLE
WHERE TASK_ = 'Excavaion'
) SRC --Alias SRC short for SouRCe table
ON ( TGT.ACT_DESC_ = SRC.ACT_DESC_ )
WHEN MATCHED THEN UPDATE
SET
TGT.EXCAVATION_QTY = SRC.QTY
TGT.EXCAVATION_APP_DATE = SRC.APP_DATE
TGT.EXCAVATION_DOC_NO = SRC.DOC_NO;
--Step 4: Update base table based on TASK_ = 'Form Work Completion'
MERGE INTO BASE_TABLE TGT --Alias TGT short for TarGeT table
USING
(
SELECT * FROM SOURCE_TABLE
WHERE TASK_ = 'Form Work Completion'
) SRC --Alias SRC short for SouRCe table
ON ( TGT.ACT_DESC_ = SRC.ACT_DESC_ )
WHEN MATCHED THEN UPDATE
SET
TGT.FORM_WORK_COMP_QTY = SRC.QTY
TGT.FORM_WORK_COMP_APP_DATE = SRC.APP_DATE
TGT.FORM_WORK_COMP_DOC_NO = SRC.DOC_NO;
I'm performing an Sqlite3 query similar to
SELECT * FROM nodes WHERE name IN ('name1', 'name2', 'name3', ...) LIMIT 1
Am I guaranteed that it will search for name1 first, name2 second, etc? Such that by limiting my output to 1 I know that I found the first hit according to my ordering of items in the IN clause?
Update: with some testing it seems to always return the first hit in the index regardless of the IN order. It's using the order of the index on name. Is there some way to enforce the search order?
The order of the returned rows is not guaranteed to match the order of the items inside the parenthesis after IN.
What you can do is use ORDER BY in your statement with the use of the function INSTR():
SELECT * FROM nodes
WHERE name IN ('name1', 'name2', 'name3')
ORDER BY INSTR(',name1,name2,name3,', ',' || name || ',')
LIMIT 1
This code uses the same list from the IN clause as a string, where the items are in the same order, concatenated and separated by commas, assuming that the items do not contain commas.
This way the results are ordered by their position in the list and then LIMIT 1 will return the 1st of them which is closer to the start of the list.
Another way to achieve the same results is by using a CTE which returns the list along with an Id which serves as the desired ordering of the results, which will be joined to the table:
WITH list(id, item) AS (
SELECT 1, 'name1' UNION ALL
SELECT 2, 'name2' UNION ALL
SELECT 3, 'name3'
)
SELECT n.*
FROM nodes n INNER JOIN list l
ON l.item = n.name
ORDER BY l.id
LIMIT 1
Or:
WITH list(id, item) AS (
SELECT * FROM (VALUES
(1, 'name1'), (2, 'name2'), (3, 'name3')
)
)
SELECT n.*
FROM nodes n INNER JOIN list l
ON l.item = n.name
ORDER BY l.id
LIMIT 1
This way you don't have to repeat the list twice.
Need help being new to "REGEXP_REPLACE".
When I do
SELECT REGEXP_REPLACE('7ELEVEN USA','[(\D^USA|^CANADA|^Canada)]','') "NAME" from dual
I get 7ELEVE and you can see that last character N is missing.
I want to replace first numbers from below & display 7-ELEVEN STORE.
20991 7-ELEVEN STORE
Any help is greatly appreciated.
Thanking in advance
I want to replace first numbers from below & display 7-ELEVEN STORE.
20991 7-ELEVEN STORE
Well, you don't even need regular expressions for that - the good, old SUBSTR + INSTR does the job just fine (that's RES2). If you want regexp, then this pattern: ^\d+ does it - it says:
^ anchor to the beginning of the string
\d+ take all digits there are (up to the first non-digit character, which is the space)
An example:
SQL> with test (col) as
2 (select '20991 7-ELEVEN STORE' from dual)
3 select
4 regexp_replace(col, '^\d+') res1,
5 substr(col, instr(col, ' ') + 1) res2
6 from test;
RES1 RES2
--------------- --------------
7-ELEVEN STORE 7-ELEVEN STORE
SQL>
[EDIT]
As of the first query you posted (I didn't understand it was the question): if you want to select the first "word" from that string, I wouldn't use REGEXP_REPLACE but (REGEXP_)SUBSTR:
SQL> with test (col) as
2 (select '7ELEVEN USA' from dual)
3 select regexp_substr(col, '\w+') res1,
4 substr(col, 1, instr(col, ' ') - 1) res2
5 from test;
RES1 RES2
------- -------
7ELEVEN 7ELEVEN
SQL>
I want to sort semicolon separated values per row in a column. Eg.
Input:
abc;pqr;def;mno
xyz;pqr;abc
abc
xyz;jkl
Output:
abc;def;mno;pqr
abc;pqr;xyz
abc
jkl;xyz
Can anyone help?
Perhaps something like this. Breaking it down:
First we need to break up the strings into their component tokens, and then reassemble them, using LISTAGG(), while ordering them alphabetically.
There are many ways to break up a symbol-separated string. Here I demonstrate the use of a hierarchical query. It requires that the input strings be uniquely distinguished from each other. Since the exact same semicolon-separated string may appear more than once, and since there is no info from the OP about any other unique column in the table, I create a unique identifier (using ROW_NUMBER()) in the most deeply nested subquery. Then I run the hierarchical query to break up the inputs and then reassemble them in the outermost SELECT.
with
test_data as (
select 'abc;pqr;def;mno' as str from dual union all
select 'xyz;pqr;abc' from dual union all
select 'abc' from dual union all
select 'xyz;jkl' from dual
)
-- End of test data (not part of the solution!)
-- SQL query begins BELOW THIS LINE.
select str,
listagg(token, ';') within group (order by token) as sorted_str
from (
select rn, str,
regexp_substr(str, '([^;]*)(;|$)', 1, level, null, 1) as token
from (
select str, row_number() over (order by null) as rn
from test_data
)
connect by level <= length(str) - length(replace(str, ';')) + 1
and prior rn = rn
and prior sys_guid() is not null
)
group by rn, str
;
STR SORTED_STR
--------------- ---------------
abc;pqr;def;mno abc;def;mno;pqr
xyz;pqr;abc abc;pqr;xyz
abc abc
xyz;jkl jkl;xyz
4 rows selected.
I have field with an integer value which means number of seconds. I would want to display it as MM:SS, without hours, so for example 6000 seconds should be shown as 100:00.
What's the easiest way to do this? As far as I see, to_char cannot be used for this.
This is a kind of workaround, as I haven't found better solution:
You could use a simple select:
test=# select (6022/60)::TEXT || ':' || (6022%60)::TEXT;
?column?
----------
100:22
(1 row)
This could be wrapped in a nice function:
CREATE FUNCTION format(integer) RETURNS TEXT AS $$
select ($1/60)::TEXT || ':' || ($1%60)::TEXT;
$$ LANGUAGE SQL STABLE;
Example of usage:
test=# SELECT format(6);
format
--------
0:6
(1 row)
test=# SELECT format(60);
format
--------
1:0
(1 row)
test=# SELECT format(600);
format
--------
10:0
(1 row)
test=# SELECT format(6000);
format
--------
100:0
(1 row)