Select all rows that have ID in materialized path - sqlite

I have this a tree structured table with a materialized path column (matpath).
The data looks like this:
ID MATPATH PARENT
---------------------
1 NULL NULL
2 1. 1
3 1.2. 2
4 1.2.3. 3
5 1.2. 2
6 1.2.3.4. 4
7 1.2.5. 5
etc
Given the ID, how can I get all elements that are above (one query) or below (anther query)?
For example, if the ID is 7, I want to select rows with IDs 1, 2 and 5 in addition to 7.
If the given ID is 3, select 1, 2 and 3. And so on.
Thank you.

First you have to decide if you want the trailing . on your materialized paths, I'll assume that you do want them because it will make life easier.
Something like this will get you the nodes below:
select id
from tree
where matpath like (
select matpath || id || '.%'
from tree
where id = X
)
Where X is the node you're interested in. Your tree looks like this:
1 --- 2 -+- 3 --- 4 --- 6
|
+- 5 --- 7
And applying the above query with a few values matches the diagram:
X | output
--+--------------
3 | 4, 6
7 |
2 | 3, 4, 5, 6, 7
Getting the nodes above a given node is easier in the client: just grab the matpath, chop off the trailing ., and then split what's left on .. SQLite's string processing support is rather limited, I can't think of a way to split the materialized path without trying to add a user-defined split function (and I'm not sure that the appropriate split could be added).
So two queries and a little bit of string wrangling outside the database will get you what you want.

Related

Does Kusto support DEFAULT values for columns?

I have a table with 3 columns :
ID
productId
customerId
1
5
1
2
4
1
3
5
1
4
4
1
I want to add a new column called ID_MOD and its value by default will be ID%X (X is a number).
Expected result for X=3 :
ID
productId
customerId
ID_MOD
1
5
1
1
2
4
1
2
3
5
1
0
4
4
1
1
I have X instances of my app and I want each instance to query specific ID_MOD values(0/1/2.../X-1).
Is it possible to use default values for columns? If it is, can the default value be calculated based on other columns ?
what you can do is create a stored function that receives x as an input parameter, and extends your table with a calculated column (at query time).
For example:
.create-or-alter function FunctionName(x:int)
{
TableName
| extend ID_MOD = ID % x
}
If you decide x always has the same value and shouldn't be parameter, you can name the function using the same name as the table, and it will 'hide' the original table.
If the logic of calculating the extended column is well-defined in advance, you can invoke it at ingestion time, using an update policy

SQLite: Number rows of query output

I am learning SQLite and I am currently posing the question whether there is a simple way of adding a sequential numbering to the output of a query. Underneath, I provide an example of what I am trying to achieve.
For instance, I have the following query:
SELECT
splTicker AS 'Ticker',
count(splTicker) AS '# of Splits'
FROM Splits
GROUP BY splTicker
ORDER BY count(splTicker) DESC, splTicker ASC;
The output of this query is as follows:
bash-3.2$ sqlite3 myShares < Queries/Split.sql
Ticker # of Splits
---------- -----------
AI.PA 7
ASML.AS 3
BN.PA 3
ALTR.LS 2
BOKA.AS 2
DG.PA 2
...
SON.LS 1
SU.PA 1
SW.PA 1
TEC.PA 1
UMI.BR 1
VIV.PA 1
VPK.AS 1
I am trying to add a sequential number to the rows to obtain the following output:
# Ticker # of Splits
-- ---------- -----------
1 AI.PA 7
2 ASML.AS 3
3 BN.PA 3
4 ALTR.LS 2
5 BOKA.AS 2
6 DG.PA 2
...
Currently, I use a workaround and add the row numbers post-query in Perl. I am posing the question whether I could do this directly in SQLite. The idea seems simple, but I have not found a solution yet. Any help would be appreciated.
Best regards,
GAM
Try this:
SELECT
(SELECT COUNT(*)
FROM Splits AS s2
WHERE s2.splTicker <= s1.splTicker) AS '#',
splTicker AS 'Ticker',
count(splTicker) AS '# of Splits'
FROM Splits s1
GROUP BY s1.splTicker
ORDER BY count(s1.splTicker) DESC, s1.splTicker ASC;

walking list using cte

Still learning SQL :)
This time I'd like to a 'linked list' walk from a table I guess using CTE.
Despite all the example on the web I could not find one simple example I could start from then peek and poke from there.
Here is my table
create table yo (id integer, nx integer)
select * from yo
id nx
---------- ----------
1 5
2 4
3 7
4 9
5 3
6 0
7 0
8 6
9 8
I'd like to get a list of 'id','nx' from yo following the next link 'nx' given a start 'id'
So a start 'id' of 1 would produce
id nx
---------- ----------
1 5
3 7
5 3
7 0
Note that 0 is a end marker.
I can't find the magic SQL for doing this
Thanx in advance
Cheers,
Phi
The first row of the list is easy:
SELECT id, nx
FROM yo
WHERE id = 1
If the nx column of the previous entry is available as list.nx, the next entry can be returned with this query:
SELECT yo.id, yo.nx
FROM yo
JOIN list ON yo.id = list.nx
Then just plug these together:
WITH RECURSIVE list(id, nx) AS (
SELECT id, nx
FROM yo
WHERE id = 1
UNION ALL
SELECT yo.id, yo.nx
FROM yo
JOIN list ON yo.id = list.nx
)
SELECT * FROM list
(This stops automatically because there is no row where id is zero; otherwise, you could add a WHERE list.nx != 0.)

How to count the number of commas in a string?

I use teradata. No access to functions, UDFs, stored procedures
Table
id attribute
1 strength, power
2 ring, wig, puff
3 strength, ring
4 puff, wig
5 power
I need to count the number of attributes
id att_number
1 2
2 3
3 2
4 2
5 1
How can I do this?
Of course your site should install at least the oTranslate and oReplace UDFs. Then it's a simple
CHAR_LENGTH(col) - CHAR_LENGTH(OTRANSLATE(col, ',',''))
Without those functions it's a pain in the <insert your favourite body part here>. Simply try to implement a REPLACE without oREPLACE, complex SQL with a horrible performance.
In your case it's a bit easier. If the maximum number is 12 the most efficient way will be a straight:
CASE
WHEN col LIKE '%,%,%,%,%,%,%,%,%,%,%,%,%' THEN 12
WHEN col LIKE '%,%,%,%,%,%,%,%,%,%,%,%' THEN 11
WHEN col LIKE '%,%,%,%,%,%,%,%,%,%,%' THEN 10
WHEN col LIKE '%,%,%,%,%,%,%,%,%,%' THEN 9
WHEN col LIKE '%,%,%,%,%,%,%,%,%' THEN 8
WHEN col LIKE '%,%,%,%,%,%,%,%' THEN 7
WHEN col LIKE '%,%,%,%,%,%,%' THEN 6
WHEN col LIKE '%,%,%,%,%,%' THEN 5
WHEN col LIKE '%,%,%,%,%' THEN 4
WHEN col LIKE '%,%,%,%' THEN 3
WHEN col LIKE '%,%,%' THEN 2
WHEN col LIKE '%,%' THEN 1
ELSE 0
END
No need for nested CASE/POSITION/SUBSTRING.
Using only index and substring functions, you would need a nested case statement which checks for a max of 12 attributes as show below. You would need to further nest the case statement to able to count upto 12 attributes, currently it can only count to a max of 2 attributes. [note: mytext=attributes]
SELECT id, mytext
,case
when index(mytext, ',')= 0 then 0
when index(mytext, ',')>0 then
case
when index(substring(mytext, index(mytext, ','), length(mytext)), ',')=0 then 1
when index(substring(mytext, index(mytext, ','), length(mytext)), ',')>0 then 2
end
end
from mydb.sd_test
order by id;
SELECT colname
,CHARACTERS(TRIM(colname)) -CHARACTERS(TRIM(OREPLACE(colname,',',''))) +1
SAMPLE 100 FROM tablename
Can use a much simpler and efficient one liner code as below:
select length(trim(regexp_replace(Column_Name,'[^,]+','')));

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