SQLite Order by WHERE IN clause [duplicate] - sqlite

This question already has answers here:
SQLite and custom order by
(3 answers)
Query Sqlite Database by specific/custom ordering?
(3 answers)
Closed 5 years ago.
I'm using SQLite and I'm providing a list of id values to retrieve. However, the order of those id values is important and I want to retrieve the records in the same order.
For example,
SELECT
*
FROM
todos
WHERE
todos.id in ( 1, 3, 2, 4 )
This returns:
1
2
3
4
But I want it to return in the same order as the provided id values, like this:
1
3
2
4
I've seen answers for MySQL and PostgreSQL but not for SQLite.

ORDER BY CASE id WHEN x THEN y
Found it in an obscure SQLite forum.
There's a neat way of ordering things that is very specific but lends itself to be programmatically generated.
To achieve the desired order, you can use the following:
SELECT
*
FROM
todos
WHERE
todos.id in ( 1, 3, 2, 4 )
ORDER BY
CASE todos.id
WHEN 1 THEN 1
WHEN 3 THEN 2
WHEN 2 THEN 3
WHEN 4 THEN 4
END
This returns the records in the following order:
1
3
2
4
As you can see, you're manually specifying the order of each record. This would be laborious if you were doing this manually, but this can be easily programmed and appended to a query, if you're using a programming language.
For example, I'm using this with Android/Java and ended up doing something like this to generate this ORDER BY clause:
String orderBy = "CASE todos.id ";
int i = 1;
for ( int id : ids ) {
orderBy = orderBy.concat( " WHEN " + id + "' THEN " + i );
i++;
}
orderBy = orderBy.concat( " END");
// Append `orderBy` to your normal SQLite query.

Related

Azure CosmosDB, count with subquery

I'm currently using CosmosDB to store some reviews data. I'm trying to retrieve the average rating from a specific date and also the number of registers that have a rating below 3.
The query that I'm using to retrieve the average rating and count the number of registers with ratings below 3 are:
**Ratings**
SELECT avg(c.x_review) as avg_x_review,
avg(c.y_review) as avg_y_review,
{date} as date
FROM c where c.date = {date}
**Criticals (below 3)**
SELECT count(1) FROM c where c.date = {date}
AND (
c.x_review < 3 OR
c.y_review < 3
)
I wanted it to be inside just one query. This data is going to be retrieved by an Azure Function HTTP Trigger, I will use and save the generated JSON from this query in another container.
I've been searching for tips but nothing seems to work, one example of a query that I'm trying to reach is:
SELECT avg(c.x_review) as avg_x_review,
avg(c.y_review) as avg_y_review,
count(SELECT * FROM c where c.date = {date}
AND (
c.x_review < 3 OR
c.y_review < 3
)
) as criticals
FROM c where c.date = {date}
...or something like that where I could have both queries inside one query alone.
I'm expecting to generate a JSON like this:
{
"avg_x_review": 5,
"avg_y_review": 4,
[...]
[...]
"criticals": 0,
"date": "2022-02-28"
}

Creating even ranges based on values in an oracle table

I have a big table which is 100k rows in size and the PRIMARY KEY is of the datatype NUMBER. The way data is populated in this column is using a random number generator.
So my question is, can there be a possibility to have a SQL query that can help me with getting partition the table evenly with the range of values. Eg: If my column value is like this:
1
2
3
4
5
6
7
8
9
10
And I would like this to be broken into three partitions, then I would expect an output like this:
Range 1 1-3
Range 2 4-7
Range 3 8-10
It sounds like you want the WIDTH_BUCKET() function. Find out more.
This query will give you the start and end range for a table of 1250 rows split into 20 buckets based on id:
with bkt as (
select id
, width_bucket(id, 1, 1251, 20) as id_bucket
from t23
)
select id_bucket
, min(id) as bkt_start
, max(id) as bkt_end
, count(*)
from bkt
group by id_bucket
order by 1
;
The two middle parameters specify min and max values; the last parameter specifies the number of buckets. The output is the rows between the minimum and maximum bows split as evenly as possible into the specified number of buckets. Be careful with the min and max parameters; I've found poorly chosen bounds can have an odd effect on the split.
This solution works without width_bucket function. While it is more verbose and certainly less efficient it will split the data as evenly as possible, even if some ID values are missing.
CREATE TABLE t AS
SELECT rownum AS id
FROM dual
CONNECT BY level <= 10;
WITH
data AS (
SELECT id, rownum as row_num
FROM t
),
total AS (
SELECT count(*) AS total_rows
FROM data
),
parts AS (
SELECT rownum as part_no, total.total_rows, total.total_rows / 3 as part_rows
FROM dual, total
CONNECT BY level <= 3
),
bounds AS (
SELECT parts.part_no,
parts.total_rows,
parts.part_rows,
COALESCE(LAG(data.row_num) OVER (ORDER BY parts.part_no) + 1, 1) AS start_row_num,
data.row_num AS end_row_num
FROM data
JOIN parts
ON data.row_num = ROUND(parts.part_no * parts.part_rows, 0)
)
SELECT bounds.part_no, d1.ID AS start_id, d2.ID AS end_id
FROM bounds
JOIN data d1
ON d1.row_num = bounds.start_row_num
JOIN data d2
ON d2.row_num = bounds.end_row_num
ORDER BY bounds.part_no;
PART_NO START_ID END_ID
---------- ---------- ----------
1 1 3
2 4 7
3 8 10

How can I concatenate(or merge) values from 2 result sets with the same PK?

I don't know if I'm being dumb here but I can't seem to find an efficient way to do this. I wrote a very long and inefficient query that does what I need, but what I WANT is a more efficient way.
I have 2 result sets that displays an ID (a PK which is generic/from the same source in both sets) and a FLAG (A - approve and V - Validate).
Result Set 1
ID FLAG
1 V
2 V
3 V
4 V
5 V
6 V
Result Set 2
ID FLAG
2 A
5 A
7 A
8 A
I want to "merge" these two sets to give me this output:
ID FLAG
1 V
2 (V/A)
3 V
4 V
5 (V/A)
6 V
7 A
8 A
Neither of the 2 result sets will at any time have all the ID's to make a simple left join with a case statement on the other result set an easy solution.
I'm currently doing a union between the two sets to get ALL the ID's. Thereafter I left join the 2 result sets to get the required '(V/A)' by use of a case statement.
There must be a more efficient way but I just can't seem to figure it out now as I'm running low on amps... I need a holiday... :-/
Thanks in advance!
Use a FULL OUTER JOIN:
SELECT ID,
CASE
WHEN t1.FLAG IS NULL THEN t2.FLAG
WHEN t2.FLAG IS NULL THEN t1.FLAG
ELSE '(' || t1.FLAG || '/' || t2.FLAG || ')'
END AS MERGED_FLAG
FROM TABLE1 t1
FULL OUTER JOIN TABLE2 t2
USING (ID)
ORDER BY ID
See this SQLFiddle.
Share and enjoy.
I think that you can use xmlagg. Here an exemple :
SELECT deptno,
SUBSTR (REPLACE (REPLACE (XMLAGG (XMLELEMENT ("x", ename)
ORDER BY ename),'</x>'),'<x>','|'),2) as concated_list
FROM emp
GROUP BY deptno
ORDER BY deptno;
Bye

Strange result in SQLite query

I´m getting a strange result from a SQLite query. The query is the next one:
SELECT rule FROM rules
WHERE idRule = (SELECT idRuleForeign FROM rulesXfilter
WHERE idFilterForeign = (SELECT idFilter FROM filters
WHERE name = 'Filter1'));
Now, let´s suppose that I have the following tables with a few rows on it.
filters rules rulesXfilter
idFilter name idRule rule idRuleForeign idFilterForeign
1 Filter1 1 Rule1 1 1
2 Filter2 2 Rule2 2 1
3 Rule3 3 1
2 2
What I get is {Rule1}, although I think I should get {Rule1, Rule2, Rule3}
What am I doing wrong?
Select idRuleForeign... returns multiple results, yes ({1, 2, 3}). However, you then say "give me the rule where idRule = {SET}", and sql doesnt like this. I believe what is happening is that it is instead taking the first result only and giving you that.
The solution is to use joins. Inner selects like that, while work most of the time, can REALLY slow down your query. If I got my syntax correct, the following should do what you need:
SELECT r.rule FROM rules r
JOIN rulesXfilter rf ON r.idRule = rf.idRuleForeign
JOIN filters f ON f.idFilter = rf.idFilterForeign
WHERE f.name = 'Filter1'

Filtering in Oracle based on a group of values contained in a list of values

I have following two tables:
ID_PERSON NAME
-----------------
1 John
2 Joe
3 Peter
ID_PERSON ID_SPECIALIZATION
------------------------------
1 5
1 6
1 7
2 5
2 1
3 6
3 10
I need to filter data based on group of ids ID_SPECIALIZATION that will be provided. For example
I want to display only those persons who has specialization in 5 and 6 so it will return only first person. In ASP.NET Web form there will be two listboxes, left and right button, in first LB there will be all possible specializations and user will choose some of them to second LB as filtering options. I have no idea how to put this filtering condition in sql query. Thanks for help.
You could use the following:
SQL> SELECT p.id_person, p.NAME
2 FROM person p
3 JOIN person_spe s ON p.id_person = s.id_person
4 WHERE id_specialization IN (5, 6)
5 GROUP BY p.id_person, p.NAME
6 HAVING COUNT(*) = 2;
ID_PERSON NAME
---------- -----
1 John
One way to do it:
SELECT
ID_PERSON
, NAME
FROM
Person AS p
WHERE EXISTS
( SELECT *
FROM
PersonSpecialization AS ps
WHERE ps.ID_PERSON = p.ID_PERSON
AND ps.ID_SPECIALIZATION = 5
)
AND EXISTS
( SELECT *
FROM
PersonSpecialization AS ps
WHERE ps.ID_PERSON = p.ID_PERSON
AND ps.ID_SPECIALIZATION = 6
)
SELECT d1.id_person, d1.name FROM tbl_table1 d1
INNER JOIN tbl_table2 d1
ON d1.ID_PERSON=d2.ID_PERSON
WHERE ID_SPECILIZATION = ?
Theres the query but I'm not sure how asp.net works and passing in the value. It might be work looking up bind variables which allows you to use place holders in the sql which oracle then caches the query and just uses the values that you pass in at run tuime using EXECUTE IMMEDIATE.

Resources