SQL last in Recursion(variable) - sql-server-2008

My recursion give me complete structure
For example:
(This example is only for KOMPARTNR=49807, but I have more KOMPARTNR where are more than 500 results.)
How real items structure looks:
49807(level 1) -> 50208(level 2) -> 520008(level 3)
49807(level 1) -> 52344(level 2)
49807(level 1) -> 20308(level 2) -> 51005(level 3)
Output looks:
KOMPARTNR ARTIKEL_NR level
49807 50208 1
49807 52344 1
49807 20308 1
50208 520008 2
520008 530000 3
52344 54500 2
20308 51005 2
51005 53250 3
I want the last records from the Structure. From the example above, only 530000, 54500 and 53250 will stand out. But it must be variable. Another KOMPARTNR have another result, that's why I wrote too "How real structure looks".
What I only want from this example:
KOMPARTNR ARTIKEL_NR level
520008 530000 3
52344 54500 2
51005 53250 3
Here is my recursion:
WITH n(KOMPARTNR, ARTIKEL_NR, level) AS
(SELECT SMSTLPOS.KOMPARTNR, SMSTLPOS.ARTNR, 1 AS level
FROM SMSTLPOS
WHERE
SMSTLPOS.KOMPARTNR='49807'
UNION ALL
SELECT SMSTLPOS1.KOMPARTNR, SMSTLPOS1.ARTNR, n.level+1
FROM SMSTLPOS as SMSTLPOS1, n
WHERE n.ARTIKEL_NR = SMSTLPOS1.KOMPARTNR
)
SELECT * FROM n
How can I get last records from the Structure?

You are almost there ...
WITH n(KOMPARTNR, ARTIKEL_NR, level) AS
(SELECT SMSTLPOS.KOMPARTNR, SMSTLPOS.ARTNR, 1 AS level
FROM SMSTLPOS
WHERE
SMSTLPOS.KOMPARTNR='49807'
UNION ALL
SELECT SMSTLPOS1.KOMPARTNR, SMSTLPOS1.ARTNR, n.level+1
FROM SMSTLPOS as SMSTLPOS1, n
WHERE n.ARTIKEL_NR = SMSTLPOS1.KOMPARTNR
)
SELECT * FROM n WHERE ARTIKEL_NR NOT IN (SELECT TOP (
(SELECT COUNT(*) FROM n) - 2
) ARTIKEL_NR
FROM n
)
And even simpler
WITH n(KOMPARTNR, ARTIKEL_NR, level) AS
(SELECT SMSTLPOS.KOMPARTNR, SMSTLPOS.ARTNR, 1 AS level
FROM SMSTLPOS
WHERE
SMSTLPOS.KOMPARTNR='49807'
UNION ALL
SELECT SMSTLPOS1.KOMPARTNR, SMSTLPOS1.ARTNR, n.level+1
FROM SMSTLPOS as SMSTLPOS1, n
WHERE n.ARTIKEL_NR = SMSTLPOS1.KOMPARTNR
)
SELECT * FROM n EXCEPT SELECT TOP (( SELECT COUNT(ARTIKEL_NR) FROM n) -2) * FROM n

Related

Dynamically fetch columns as JSON in oracle

I have to get some columns as is and some columns from a query as JSON document. The as is column names are known to me but rest are dynamic columns so there are not known beforehand.
Below is the query like
select col1,col2,col3,
sum(col4) as col4,
sum(col5) as col5
from my_table
group by col1,col2,col3;
here col4,col5 names are unknown to me as they are been fetched dynamically.
Suppost my_table data looks like
The expected result is like below
I tried
select JSON_OBJECT(*) from
(
select col1,col2,col3,
sum(col4) as col4,
sum(col5) as col5
from my_table
group by col1,col2,col3
);
But obviously it does not yield expected output.
I'm on 19c DB version 19.17
Any help or suggestion would be great help!
It's kinda hacky, but you could:
Use json_object(*) to convert the whole row to json
Pass the result of this json_transform*, which you can use to remove unwanted attributes
So you could do something like:
with rws as (
select mod ( level, 2 ) col1, mod ( level, 3 ) col2,
level col3, level col4
from dual
connect by level <= 10
), grps as (
select col1,col2,
sum(col3) as col3,
sum(col4) as col4
from rws
group by col1,col2
)
select col1,col2,
json_transform (
json_object(*),
remove '$.COL1',
remove '$.COL2'
) json_data
from grps;
COL1 COL2 JSON_DATA
---------- ---------- ------------------------------
1 1 {"COL3":8,"COL4":8}
0 2 {"COL3":10,"COL4":10}
1 0 {"COL3":12,"COL4":12}
0 1 {"COL3":14,"COL4":14}
1 2 {"COL3":5,"COL4":5}
0 0 {"COL3":6,"COL4":6}
json_transform is a 21c feature that's been backported to 19c in 19.10.
You may achieve this by using Polymorphic Table Function available since 18c.
Define the function that will project only specific columns and serialize others into JSON. An implementation is below.
PTF package (function implementation).
create package pkg_jsonify as
/*Package to implement PTF.
Functions below implement the API
described in the DBMS_TF package*/
function describe(
tab in out dbms_tf.table_t,
keep_cols in dbms_tf.columns_t
) return dbms_tf.describe_t
;
procedure fetch_rows;
end pkg_jsonify;
/
create package body pkg_jsonify as
function describe(
tab in out dbms_tf.table_t,
keep_cols in dbms_tf.columns_t
) return dbms_tf.describe_t
as
add_cols dbms_tf.columns_new_t;
new_col_cnt pls_integer := 0;
begin
for i in 1..tab.column.count loop
/*Initially remove column from the output*/
tab.column(i).pass_through := FALSE;
/*and keep it in the row processing (to be available for serialization*/
tab.column(i).for_read := TRUE;
for j in 1..keep_cols.count loop
/*If column is in a projection list, then remove it
from processing and pass it as is*/
if tab.column(i).description.name = keep_cols(j)
then
tab.column(i).pass_through := TRUE;
/*Skip column in the row processing (JSON serialization)*/
tab.column(i).for_read := FALSE;
end if;
end loop;
end loop;
/*Define new output column*/
add_cols := dbms_tf.columns_new_t(
1 => dbms_tf.column_metadata_t(
name => 'JSON_DOC_DATA',
type => dbms_tf.type_clob
)
);
/*Return the list of new cols*/
return dbms_tf.describe_t(
new_columns => add_cols
);
end;
procedure fetch_rows
/*Process rowset and serialize cols*/
as
rowset dbms_tf.row_set_t;
num_rows pls_integer;
new_col dbms_tf.tab_clob_t;
begin
/*Get rows*/
dbms_tf.get_row_set(
rowset => rowset,
row_count => num_rows
);
for rn in 1..num_rows loop
/*Calculate new column value in the same row*/
new_col(rn) := dbms_tf.row_to_char(
rowset => rowset,
rid => rn,
format => dbms_tf.FORMAT_JSON
);
end loop;
/*Put column to output*/
dbms_tf.put_col(
columnid => 1,
collection => new_col
);
end;
end pkg_jsonify;
/
PTF function definition based on the package.
create function f_cols_to_json(tab in table, cols in columns)
/*Function to serialize into JSON using PTF*/
return table pipelined
row polymorphic using pkg_jsonify;
/
Demo.
create table sample_tab
as
select
trunc(level/10) as id
, mod(level, 3) as id2
, level as val1
, level * level as val2
from dual
connect by level < 40
with prep as (
select
id
, id2
, sum(val1) as val1_sum
, max(val2) as val2_max
from sample_tab
group by
id
, id2
)
select *
from table(f_cols_to_json(prep, columns(id, id2)))
ID
ID2
JSON_DOC_DATA
0
1
{"VAL1_SUM":12, "VAL2_MAX":49}
0
2
{"VAL1_SUM":15, "VAL2_MAX":64}
0
0
{"VAL1_SUM":18, "VAL2_MAX":81}
1
1
{"VAL1_SUM":58, "VAL2_MAX":361}
1
2
{"VAL1_SUM":42, "VAL2_MAX":289}
1
0
{"VAL1_SUM":45, "VAL2_MAX":324}
2
2
{"VAL1_SUM":98, "VAL2_MAX":841}
2
0
{"VAL1_SUM":72, "VAL2_MAX":729}
2
1
{"VAL1_SUM":75, "VAL2_MAX":784}
3
0
{"VAL1_SUM":138, "VAL2_MAX":1521}
3
1
{"VAL1_SUM":102, "VAL2_MAX":1369}
3
2
{"VAL1_SUM":105, "VAL2_MAX":1444}
with prep as (
select
id
, id2
, sum(val1) as val1_sum
, max(val2) as val2_max
from sample_tab
group by
id
, id2
)
select *
from table(f_cols_to_json(prep, columns(id)))
ID
JSON_DOC_DATA
0
{"ID2":1, "VAL1_SUM":12, "VAL2_MAX":49}
0
{"ID2":2, "VAL1_SUM":15, "VAL2_MAX":64}
0
{"ID2":0, "VAL1_SUM":18, "VAL2_MAX":81}
1
{"ID2":1, "VAL1_SUM":58, "VAL2_MAX":361}
1
{"ID2":2, "VAL1_SUM":42, "VAL2_MAX":289}
1
{"ID2":0, "VAL1_SUM":45, "VAL2_MAX":324}
2
{"ID2":2, "VAL1_SUM":98, "VAL2_MAX":841}
2
{"ID2":0, "VAL1_SUM":72, "VAL2_MAX":729}
2
{"ID2":1, "VAL1_SUM":75, "VAL2_MAX":784}
3
{"ID2":0, "VAL1_SUM":138, "VAL2_MAX":1521}
3
{"ID2":1, "VAL1_SUM":102, "VAL2_MAX":1369}
3
{"ID2":2, "VAL1_SUM":105, "VAL2_MAX":1444}
fiddle

How to use NOT IN in binary tree problem using MySQL

I am trying to solve this problem,
You are given a table, BST, containing two columns: N and P, where N
represents the value of a node in Binary Tree, and P is the parent of
N.
Write a query to find the node type of Binary Tree ordered by the
value of the node. Output one of the following for each node:
Root: If node is root node.
Leaf: If node is leaf node.
Inner: If node is neither root nor leaf node.
Input:
Desired Output:
1 Leaf
2 Inner
3 Leaf
5 Root
6 Leaf
8 Inner
9 Leaf
This is my query, can anyone tell me why it's not working?
select case
when P is NULL then CONCAT_WS(" ", N, 'Root')
when N not in (SELECT DISTINCT P FROM BST) then CONCAT_WS(" ", N, 'Leaf')
else CONCAT_WS(" ", N, 'Inner')
end
from BST ORDER BY N ASC;
You have NULL inside the P column in which case an expression such as:
1 NOT IN (NULL, 2, 8, 5)
will return unknown instead of the "expected" result true (ref).
The solution is to make a subtle change like so:
N NOT IN (SELECT P FROM BST WHERE P IS NOT NULL)
Or use an EXISTS query:
NOT EXISTS (SELECT * FROM BST AS bst2 WHERE bst2.P = bst.N)
SELECT n,
ELT((1 + (2 * (t1.p IS NULL)) + (EXISTS (SELECT NULL FROM BST t2 WHERE t1.n=t2.p))), 'Leaf', 'Inner', 'Single', 'Root') type
FROM BST t1
ORDER BY n;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=287b1de2b2bb532d73619c19bcf8a86b
I add one more option 'Single' - the case when the node is root and leaf at the same time (node 4 in my fiddle).

How to get number of steps to get a sub of tree

How can I do to get number of steps to get a sub of tree .
for example I have a table like this:
id title parent_id
1 A 0
2 B 0
3 C 1
4 F 3
5 O 3
6 D 2
7 J 6
8 T 2
9 P 8
A // 1 step
C //2 step
F //3 step
O //3 step
B //1 step
D //2 step
J //3 step
T //2 step
P //3 step
for example if I give a number like 1 (id = 1 ), it should return 1 and id=6 it should return 2 as step.
my DBMS is MySQL.
It can be a recursive stored procedure, if your tree is not very deep. Something like this (with addition of proper condition handlers), or anything of the same kind, it can be written in various ways:
DELIMITER $
CREATE PROCEDURE depth(IN n INT, OUT depth INT)
BEGIN
DECLARE parent INT DEFAULT 0;
SET max_sp_recursion_depth=255;
SELECT parent_id INTO parent FROM t WHERE id=n;
IF parent = 0 THEN SET depth = 1;
ELSE CALL depth(parent,depth); SET depth = depth + 1;
END IF;
END $
DELIMITER ;
CALL(6,#depth);
SELECT #depth;
Also, MariaDB 10.2 supports recursive CTE. It's an early beta now, so it's not good for production, but if you're only evaluating your options, you can try it out. This should work:
WITH RECURSIVE tree(id,parent_id,depth) AS
(
SELECT id, parent_id, 1 from t WHERE parent_id=0
UNION ALL
SELECT t.id, t.parent_id, depth+1 FROM t JOIN tree ON tree.id = t.parent_id
) SELECT * FROM tree WHERE id = 6;

Fastest way to calculate correlation in every row?

Well, I have a table data of millions of rows. I want to carry out correlation study for every row (from the 1st to the current row minus 1). For e.g. the 1st rows is omitted. The 2nd row's result column is to be supplied with the correlation using the 1st row. The 3rd row's result column is to be supplied with the correlation using the 1st and 2nd row. And so on.
Correlation for the entire table can be calculated using:
SELECT (Count(*)*Sum(x*y)-Sum(x)*Sum(y))/
(sqrt(Count(*)*Sum(x*x)-Sum(x)*Sum(x))*
sqrt(Count(*)*Sum(y*y)-Sum(y)*Sum(y))) AS TotalCorelation FROM Data;
I want to avoid using Joins as much as possible as it takes lots of time, sometimes even timeout error, above 300 seconds). What's the other alternative?
Example table Data Structure:
id, x, y, result
1 , 4, 2, null
2 , 6, 3, -0.2312
3 , 5, 5, 0.42312
4 , 6, 2, -0.5231
5 , 5, 5, 0.22312
6 , 3, 7, -0.2312
7 , 2, 9, 0.42231
8 , 7, 2, 0.32253
9 , 9, 5, 0.32431
id : primary key
x and y : The data
result: correlation
I think this is it:
SELECT d2.ID, d2.x, d2.y, d2.result,
(Count(*)*Sum(d1.x*d1.y)-Sum(d1.x)*Sum(d1.y))/
(sqrt(Count(*)*Sum(d1.x*d1.x)-Sum(d1.x)*Sum(d1.x))*
sqrt(Count(*)*Sum(d1.y*d1.y)-Sum(d1.y)*Sum(d1.y))) AS TotalCorelation
FROM Data d1
RIGHT JOIN Data d2 ON d1.id < d2.id
GROUP BY d2.ID
ORDER BY d2.ID
Without a closed form for calculating correlation of N+1 from N rows, you have to use a quadratic join like this.
I'm assuming that your basic formula is correct. But I'm not sure it is -- when I just run it on the total dataset, I don't get the result 0.32431, I get -0.552773693079.
Here's a linear implementation:
SET #SumX = 0;
SET #SumY = 0;
SET #Count = 0;
SET #SumX2 = 0;
SET #SumY2 = 0;
SET #SumXY = 0;
SELECT id, x, y,
#SumX := #SumX + x AS SumX,
#SumY := #SumY + y AS SumY,
#Count := #Count + 1 AS ct,
#SumX2 := #SumX2 + x*x AS SumX2,
#SumY2 := #SumY2 + y*y AS SumY2,
#SumXY := #SumXY + x*y AS SumXY,
IF(#Count > 1,
(#Count*#SumXY-#SumX*#SumY)/
(sqrt(#Count*#SumX2-#SumX*#SumX)*
sqrt(#Count*#SumY2-#SumY*#SumY)), NULL) AS TotalCorelation
FROM DATA
ORDER BY id
SQLFIDDLE

SQL Server: calculate field data from fields in same table but different set of data

I was looking around and found no solution to this. I´d be glad if someone could help me out here:
I have a table, e.g. that has among others, following columns:
Vehicle_No, Stop1_depTime, Segment_TravelTime, Stop_arrTime, Stop_Sequence
The data might look something like this:
Vehicle_No Stop1_DepTime Segment_TravelTime Stop_Sequence Stop_arrTime
201 13000 60 1
201 13000 45 2
201 13000 120 3
201 13000 4
202 13300 240 1
202 13300 60 2
...
and I need to calculate the arrival time at each stop from the departure time at the first stop and the travel times in between for each vehicle. What I need in this case would look like this:
Vehicle_No Stop1_DepTime Segment_TravelTime Stop_Sequence Stop_arrTime
201 13000 60 1
201 13000 45 2 13060
201 13000 120 3 13105
201 13000 4 13225
202 13300 240 1
202 13300 60 2 13540
...
I have tried to find a solution for some time but was not successful - Thanks for any help you can give me!
Here is the query that still does not work - I am sure I did something wrong with getting the table from the database into this but dont know where. Sorry if this is a really simple error, I have just begun working with MSSQL.
Also, I have implemented the solution provided below and it works. At this point I mainly want to understand what went wrong here to learn about it. If it takes too much time, please do not bother with my question for too long. Otherwise - thanks a lot :)
;WITH recCTE
AS
(
SELECT ZAEHL_2011.dbo.L32.Zaehl_Fahrt_Id, ZAEHL_2011.dbo.L32.PlanAbfahrtStart, ZAEHL_2011.dbo.L32.Fahrzeit, ZAEHL_2011.dbo.L32.Sequenz, ZAEHL_2011.dbo.L32.PlanAbfahrtStart AS Stop_arrTime
FROM ZAEHL_2011.dbo.L32
WHERE ZAEHL_2011.dbo.L32.Sequenz = 1
UNION ALL
SELECT t. ZAEHL_2011.dbo.L32.Zaehl_Fahrt_Id, t. ZAEHL_2011.dbo.L32.PlanAbfahrtStart, t. ZAEHL_2011.dbo.L32.Fahrzeit,t. ZAEHL_2011.dbo.L32.Sequenz, r.Stop_arrTime + r. ZAEHL_2011.dbo.L32.Fahrzeit AS Stop_arrTime
FROM recCTE AS r
JOIN ZAEHL_2011.dbo.L32 AS t
ON t. ZAEHL_2011.dbo.L32.Zaehl_Fahrt_Id = r. ZAEHL_2011.dbo.L32.Zaehl_Fahrt_Id
AND t. ZAEHL_2011.dbo.L32.Sequenz = r. ZAEHL_2011.dbo.L32.Sequenz + 1
)
SELECT ZAEHL_2011.dbo.L32.Zaehl_Fahrt_Id, ZAEHL_2011.dbo.L32.PlanAbfahrtStart, ZAEHL_2011.dbo.L32.Fahrzeit, ZAEHL_2011.dbo.L32.Sequenz, ZAEHL_2011.dbo.L32.PlanAbfahrtStart,
CASE WHEN Stop_arrTime = ZAEHL_2011.dbo.L32.PlanAbfahrtStart THEN NULL ELSE Stop_arrTime END AS Stop_arrTime
FROM recCTE
ORDER BY ZAEHL_2011.dbo.L32.Zaehl_Fahrt_Id, ZAEHL_2011.dbo.L32.Sequenz
A recursive CTE solution - assumes that each Vehicle_No appears in the table only once:
DECLARE #t TABLE
(Vehicle_No INT
,Stop1_DepTime INT
,Segment_TravelTime INT
,Stop_Sequence INT
,Stop_arrTime INT
)
INSERT #t (Vehicle_No,Stop1_DepTime,Segment_TravelTime,Stop_Sequence)
VALUES(201,13000,60,1),
(201,13000,45,2),
(201,13000,120,3),
(201,13000,NULL,4),
(202,13300,240,1),
(202,13300,60,2)
;WITH recCTE
AS
(
SELECT Vehicle_No, Stop1_DepTime, Segment_TravelTime,Stop_Sequence, Stop1_DepTime AS Stop_arrTime
FROM #t
WHERE Stop_Sequence = 1
UNION ALL
SELECT t.Vehicle_No, t.Stop1_DepTime, t.Segment_TravelTime,t.Stop_Sequence, r.Stop_arrTime + r.Segment_TravelTime AS Stop_arrTime
FROM recCTE AS r
JOIN #t AS t
ON t.Vehicle_No = r.Vehicle_No
AND t.Stop_Sequence = r.Stop_Sequence + 1
)
SELECT Vehicle_No, Stop1_DepTime, Segment_TravelTime,Stop_Sequence, Stop1_DepTime,
CASE WHEN Stop_arrTime = Stop1_DepTime THEN NULL ELSE Stop_arrTime END AS Stop_arrTime
FROM recCTE
ORDER BY Vehicle_No, Stop_Sequence
EDIT
Corrected version of OP's query - note that it's not necessary to fully qualify the column names:
;WITH recCTE
AS
(
SELECT Zaehl_Fahrt_Id, PlanAbfahrtStart, Fahrzeit, L32.Sequenz, PlanAbfahrtStart AS Stop_arrTime
FROM ZAEHL_2011.dbo.L32
WHERE Sequenz = 1
UNION ALL
SELECT t.Zaehl_Fahrt_Id, t.PlanAbfahrtStart, t.Fahrzeit,t.Sequenz, r.Stop_arrTime + r.Fahrzeit AS Stop_arrTime
FROM recCTE AS r
JOIN ZAEHL_2011.dbo.L32 AS t
ON t.Zaehl_Fahrt_Id = r.Zaehl_Fahrt_Id
AND t.Sequenz = r.Sequenz + 1
)
SELECT Zaehl_Fahrt_Id, PlanAbfahrtStart, Fahrzeit, Sequenz, PlanAbfahrtStart,
CASE WHEN Stop_arrTime = PlanAbfahrtStart THEN NULL ELSE Stop_arrTime END AS Stop_arrTime
FROM recCTE
ORDER BY Zaehl_Fahrt_Id, Sequenz
I'm quite sure this works:
SELECT a.Vehicle_No, a.Stop1_DepTime,
a.Segment_TravelTime, a.Stop_Sequence, a.Stop1_DepTime +
(SELECT SUM(b.Segment_TravelTime) FROM your_table b
WHERE b.Vehicle_No = a.Vehicle_No AND b.Stop_Sequence < a.Stop_Sequence)
FROM your_table a
ORDER BY a.Vehicle_No