Recursive SQL query that finds path - sql

i am trying to solve this problem and i am given a database table such as: create table file(id int, parentid int, name varchar(1024), size int, type
char(1));
I have to write a single (recursive) database query to list FULL PATH of all files. [assume type is either "F" or "D" for file or directory]. Your query should give you similar output to unix command: "find . -type f".
I wrote something like this but, I am not sure if this is what the question is asking for me to do since I am inexperienced in Unix. Any help would be greatly appreciated. Thanks
WITH RECURSIVE search_path (path_ids, length, is_visited) AS
(
SELECT
ARRAY[node_id, destination_node_id],
link_length,
node_id = destination_node_id
FROM
node_links_view
UNION ALL
SELECT
path_ids || d.destination_node_id,
f.length + d.link_length,
d.destination_node_id = ANY(f.path_ids)
FROM
node_links_view d,
search_path f
WHERE
f.path_ids[array_length(path_ids, 1)] = d.node_id
AND NOT f.is_visited
)
SELECT * FROM search_path
WHERE path_ids[1] = 1 AND path_ids[array_length(path_ids, 1)] = 6
ORDER BY length;

Related

Selecting with INT value against STR field

I'm trying to select records from a table using SQL Developer based on an INT value (in one file) to a STR (in the other file) and I'm using a nested(?) query:
select *
from proddta.FQ584871
where tdrscn in (select rmrscn from proddta.f48310)
and tdan8 > 1 and tdeqhr > 0
and tddoco not in (select glsbl from proddta.f0911)
In this example the TDDOCO is an INT and the GLSBL is a STR. The match would be TDDOCO = 456123 to the GLSBL = 00456123.
Thank you for any assistance
I've tried to CAST and the STR and VARCHAR but they don't appear to work when I'm trying to convert the TDDOCO.
I've tried:
tddoco not in (select to_number(ltrim(glsbl,'0')) from proddta.f0911
and I've tried:
to_char(tddoco) not in (select glsbl from proddta.f0911)
and I get an invalid number error.

SQL Is it possible to incorporate a SELECT with a REPLACE?

I'm using MS SQL Server 2019
I have a string in a table (tblJobCosts) that has its own ID like this:
TextID jobText
1 Total Cost for job is £[]. This includes VAT
How do I update the value stored in the brackets based on the value from another table?
The end result would look like this:
Total Cost for job is £500. This includes VAT
I thought I could incorporate a SELECT with a REPLACE but this does not seem possible:
DECLARE #JobNum INT = 123;
UPDATE dbo.JobCosts
SET jobText = REPLACE (jobText,'[]',
SELECT JH.jobCost
FROM dbo.JobHead AS JH
WHERE (JH.JobNo = #JobNum)
) AND TextID = 1
If I run the above I receive the error:
Incorrect syntax near the keyword 'SELECT'.
Is it possible to incorporate a SELECT with a REPLACE?
I think that you cannot call a select statement in the replace function.
I would try something like that:
UPDATE dbo.JobCosts
SET jobText = REPLACE (jobText,'[]',k.the_cost) from
( SELECT JH.jobCost as the_cost
FROM dbo.JobHead AS JH
WHERE (JH.JobNo = #JobNum)
)k
where TextID = 1

postgres access a cte inside a view

is it possible, in postgresql - to access a cte defined in a view?
By that I mean - if you have the following:
create view my_view as
with
blah as (select 1 as x, 2 as y, 3 as z)
select
x*x as x_squared,
y*y as y_squared,
z*z as z_squared
from
blah
is there any way from outside of getting at blah? eg looking for something like:
select * from my_view.blah
Basically we have large views that use a number of complicated CTE's - and it's quite difficult sometimes to troubleshoot them without splitting them all out into separate smaller views [yes, I would prefer to just keep it like that, but I don't have that option]
I know I will be able to do it by making a stored proc that pulls out the view definition - extracts the with clauses, parses up to the blah definition, changes that to the main select, gets rid of the rest, and then does the query - but that all seems like a lot of work. Am hoping there's a built-in way?
You can create the CTE as a view by itself. For example:
create table a (b int);
insert into a (b) values (1), (50), (200), (350), (1000);
create view blah as
select * from a where b > 100;
Anf then base your original view on this new intermediate one to avoid repeating code:
create view my_view as
select * from blah where b < 500;
See running example at DB Fiddle.
ok - so I have a sort of solution - it's a not a function - it's a procedure that turns a cte into a materialized_view.
I first wanted to formulate it as function, so you could say:
select * from cte_from( 'my_real_view', 'the_cte' )
but it appears that a function needs its schema defined in advance, which is obviously impossible in this case. If anyone can suggest a hack to make it closer to above, I'd apperciate it. But anyway - bottom line this works:
create procedure from_cte(view_schema text, view_name text, cte_name text, materialized_view_name text) as
$func$
declare
_code text;
_others text;
_script text;
begin
execute format('drop materialized view if exists %s', materialized_view_name);
with recursive
string_provider as (
select view_definition as the_string,
position(concat(cte_name, ' as (') in lower(view_definition)) + length(cte_name) + 5 as start_location
from information_schema.views
where table_name = view_name
and table_schema = view_schema
),
string_walk as (
select start_location as x,
1 as brackets
from string_provider
union
select x + 1,
new_brackets
from string_provider,
string_walk,
lateral (select case
when substring(the_string from x + 1 for 1) = '(' then brackets + 1
when substring(the_string from x + 1 for 1) = ')' then brackets - 1
else brackets
end as new_brackets
) calculated
where new_brackets != 0
and x < length(the_string)
)
select substring(the_string from start_location for 1 + max(x) - start_location),
trim(substring(the_string from 0 for start_location - length(cte_name) - 5))
into _code, _others
from string_walk,
string_provider
group by the_string, start_location;
if length(_others) < 5 then
select _code into _script;
else
select concat(substring(_others from 0 for length(_others)), ' ', _script) into _script;
end if;
execute format('create materialized view %s as ( %s )', materialized_view_name, _code);
end
$func$ language plpgsql;

How to split a column by the number of white spaces in it with SQL?

I've got a single column that contains a set of names in it. I didn't design the database so that it contains multiple values in one column, but as it is I've got to extract that information now.
The problem is that in one field I've got multiple values like in this example:
"Jack Tom Larry Stan Kenny"
So the first three should be one group, and the other ones on the far right are another group. (Basically the only thing that separates them in the column is a specific number of whitespace between them, let's say 50 characters.)
How can I split them in pure SQL, so that I can get two columns like this:
column1 "Jack Tom Larry"
column2 "Stan Kenny"
A fairly simplistic answer would be to use a combination of left(), right() and locate(). Something like this (note I've substituted 50 spaces with "XXX" for readability):
declare global temporary table session.x(a varchar(100))
on commit preserve rows with norecovery;
insert into session.x values('Jack Tom LarryXXXStan Kenny');
select left(a,locate(a,'XXX')-1),right(a,length(a)+1-(locate(a,'XXX')+length('XXX'))) from session.x;
If you need a more general method of extracting the nth field from a string with a given separator, a bit like the split_part() function in PostgreSQL, in Ingres your options would be:
Write a user defined function using the Object Management Extension (OME). This isn't entirely straightforward but there is an excellent example in the wiki pages of Actian's community site to get you started:
http://community.actian.com/wiki/OME:_User_Defined_Functions
Create a row-producing procedure. A bit more clunky to use than an OME function, but much easier to implement. Here's my attempt at such a procedure, not terribly well tested but it should serve as an example. You may need to adjust the widths of the input and output strings:
create procedure split
(
inval = varchar(200) not null,
sep = varchar(50) not null,
n = integer not null
)
result row r(x varchar(200)) =
declare tno = integer not null;
srch = integer not null;
ptr = integer not null;
resval = varchar(50);
begin
tno = 1;
srch = 1;
ptr = 1;
while (:srch <= length(:inval))
do
while (substr(:inval, :srch, length(:sep)) != :sep
and :srch <= length(:inval))
do
srch = :srch + 1;
endwhile;
if (:tno = :n)
then
resval=substr(:inval, :ptr, :srch - :ptr);
return row(:resval);
return;
endif;
srch = :srch + length(:sep);
ptr = :srch;
tno = :tno + 1;
endwhile;
return row('');
end;
select s.x from session.x t, split(t.a,'XXX',2) s;

How to search string to find data after end of pattern of characters (SQL DB2)

I need to find the next single event that appears after the last occurrence of the following pattern of events "5065|5373|5373". My problem is that the pattern can be in the string 1 to n times. Here's an example of the some data that I have to search through.
The events in BOLD are what i would be looking for.
5065|5373|5373|5065|5373|5373|5065|5373|5373|5509|5329|5321
5065|5373|5373|5065|5373|5373|5509|5270|5373|5373|5373|5080|5081|5013|5040|5295|5321
5065|5373|5373|5295|5323|5321
Any help would be greatly appreciated!
If you can't create a new stored procedure or UDF, here's a recursive query that'll do it for you:
WITH Recurs(id, index, token, source) as (
SELECT id, LOCATE('5065|5373|5373|', M.PATH_2), '', M.PATH_2
FROM M
UNION ALL
SELECT id, LOCATE('5065|5373|5373|', source, index + 15),
SUBSTR(source, index + 15, 4), source
FROM Recurs
WHERE index > 0)
SELECT *
FROM Recurs
WHERE index = 0
Which yields the expected:
ID INDEX TOKEN SOURCE
3 0 5295 5065|5373|5373|5295|5323|5321
2 0 5509 5065|5373|5373|5065|5373|5373|5509|5270|5373|5373|5373|5080|5081|5013|5040|5295|5321
1 0 5509 5065|5373|5373|5065|5373|5373|5065|5373|5373|5509|5329|5321
One fairly straightforward way to do this is with a recursive common table expression (CTE):
CREATE FUNCTION localutil.locatelastmatch(
searchparm VARCHAR(4000), inputparm VARCHAR(4000)
)
RETURNS SMALLINT
LANGUAGE SQL
RETURN
WITH rcurs(counter, output ) AS (
VALUES (0,0)
UNION ALL
SELECT counter+1, LOCATE(searchparm,inputparm,counter+1)
FROM rcurs
WHERE counter < LENGTH(inputparm) AND counter < 32767
)
SELECT MAX(output) FROM rcurs
;
It may not be the cheapest way to find the last matching occurrence, but it's at least a contender for it. By burying the complexity into a scalar user-defined function (UDF), you won't have to introduce the SQL recursion into every query that needs to search for the last instance of a pattern.
Here's how it works against your sample strings:
WITH originput(val) as (VALUES
('5065|5373|5373|5065|5373|5373|5065|5373|5373|5509|5329|5321'),
('5065|5373|5373|5065|5373|5373|5509|5270|5373|5373|5373|5080|5081|5013|5040|5295|5321'),
('5065|5373|5373|5295|5323|5321')
)
SELECT LENGTH(val) AS inputlength,
localutil.locatelastmatch( '5065|5373|5373|', val ) AS finaloffset,
SUBSTR(val, localutil.locatelastmatch( '5065|5373|5373|', val )
+ LENGTH( '5065|5373|5373|' ), 4) AS nextitem
FROM originput
;
INPUTLENGTH FINALOFFSET NEXTITEM
----------- ----------- --------
59 31 5509
84 16 5509
29 1 5295