Multiple clause with lines SQL - sql

I have implemented the following function in a functional way but with the problem that I had implemented it with temporary tables and inserts.
Since I want to optimize the code, I have decided to try the SQL with as .. statements.
The downside is that the way to implement it with SQL with as statements is different and I'm opting for this output:
ERROR: syntax error at end of input LINE 48: ...r = p_id_var AND
fvr.utz BETWEEN p_utz_begin AND p_utz_end);
^ SQL state: 42601 Character: 1754
This is the code:
CREATE OR REPLACE FUNCTION tlm.main_dash_tele_freq_blackout(
p_id_unit integer,
p_utz_begin timestamp without time zone,
p_utz_end timestamp without time zone)
RETURNS TABLE(can_freq interval, can_blackout interval, gps_freq interval, gps_blackout interval, chargeloss boolean)
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
DECLARE
CAN_freq interval;
CAN_blackout interval;
CAN_chargeloss boolean;
GPS_freq interval;
GPS_blackout interval;
max_diff integer;
p_id_var integer;
BEGIN
p_id_var = 1001;
with main_dash_tele_freq_blackout_first_reading as (
SELECT fvr.utz, fvr.val FROM var.oper_readings fvr WHERE fvr.id_unit = p_id_unit AND fvr.id_var = p_id_var AND fvr.utz BETWEEN p_utz_begin AND p_utz_end
),
main_dash_tele_freq_blackout_second_reading as (
SELECT fr.utz , fr.val FROM main_dash_tele_freq_blackout_first_reading fr WHERE fr.id != 1),
main_dash_tele_freq_blackout_result_reading as (
SELECT ff.utz, ss.utz, (ss.utz - ff.utz), (ss.val - ff.val) FROM main_dash_tele_freq_blackout_first_reading ff FULL JOIN main_dash_tele_freq_blackout_second_reading ss ON ff.id = ss.id
)
;
CAN_freq = (SELECT AVG(diff) FROM main_dash_tele_freq_blackout_result_reading WHERE diff < '00:10:00');
CAN_blackout = (SELECT AVG(diff) FROM main_dash_tele_freq_blackout_result_reading WHERE diff > '00:10:00' AND (diff_val > 1 OR diff_val < -1));
CAN_chargeloss = (SELECT (MAX(diff_val)>10) FROM main_dash_tele_freq_blackout_result_reading WHERE diff > '00:10:00' AND (diff_val > 1 OR diff_val < -1));
------------------------------------------------------ Similar case for this variables ------------------------------------------------
with main_dash_tele_freq_blackout_first_GPS_reading as (
SELECT fvr.utz, fvr.lat, fvr.lon FROM var.oper_geo_readings fvr WHERE fvr.id_unit = p_id_unit AND fvr.utz BETWEEN p_utz_begin AND p_utz_end
),
main_dash_tele_freq_blackout_second_GPS_reading as (
SELECT fr.utz , fr.lat, fr.lon FROM main_dash_tele_freq_blackout_first_GPS_reading fr WHERE fr.id != 1
),
main_dash_tele_freq_blackout_result_GPS_reading as (
SELECT ff.utz, ss.utz, (ss.utz - ff.utz), (ss.lat - ff.lat), (ss.lon - ff.lon) FROM main_dash_tele_freq_blackout_first_GPS_reading ff FULL JOIN main_dash_tele_freq_blackout_second_GPS_reading ss ON ff.id = ss.id
);
GPS_freq = (SELECT AVG(diff) FROM main_dash_tele_freq_blackout_result_GPS_reading WHERE diff < '00:10:00');
GPS_blackout = (SELECT AVG(diff) FROM main_dash_tele_freq_blackout_result_GPS_reading WHERE diff > '00:10:00');
RETURN QUERY (SELECT CAN_freq, CAN_blackout, GPS_freq, GPS_blackout, CAN_chargeloss );
END
$BODY$;

You can use like as these syntaxes:
declare variable1 integer;
declare variable2 integer;
with
tb1(a) as (
select 1
union all
select 2
union all
select 3
),
tb2(a) as (
select 5
union all
select 10
union all
select 15
)
select (select sum(tb1.a) from tb1) into variable1, (select sum(tb2.a) from tb2) into variable2;
return query
select variable1, variable2
or
return query
with
tb1(a) as (
select 1
union all
select 2
union all
select 3
),
tb2(a) as (
select 5
union all
select 10
union all
select 15
)
select (select sum(tb1.a) from tb1), (select sum(tb2.a) from tb2);

Related

How can i tune this select statement to prevent it from having select from same table many times?

I need to tune this sql statement provided by developer, how can I speed it up and prevent the HAVING part from select from same users table many time?
SELECT *
FROM (
SELECT t.*, ROWNUM pageination__row__123__
FROM (
SELECT *
FROM (
SELECT SUM(rcm) sum_rcm, SUM(real_amount) sum_ra, MIN(min_share) min_share, MAX(max_share) max_share, SUM(DECODE(rcm_flag, 'Y', 0, rcm)) rcm_n, b.parentid
FROM bet_total b
WHERE draw_date BETWEEN :1 AND :2
GROUP BY parentid
HAVING (
parentid
IN (
SELECT id FROM users u2,
(
SELECT u3.path
FROM users u3
WHERE u3.type = 2 AND u3.lv = 1 AND u3.tesing = 0 AND u3.user_key = :3
) q
WHERE u2.path BETWEEN q.path AND q.path || chr(to_number('FFFFFFFF', 'xxxxxxxx')) AND u2.type = 2
) AND SUM(rcm) > :4
)
) b1, users u5
WHERE (b1.parentid = u5.id AND rcm_n > 0 )
ORDER BY u5.path
) t
) t
WHERE t.pageination__row__123__ <= :5;
SQL Execution Plan
Index of the table Bet_total
Index of table users
You could try using the WITH-Clause.

when I'm using SQL Navigator my query is working but when I call from frontend my query returns 0

when I'm using SQL Navigator my query is working but when I call from frontend my query returns 0
This my query:
select *
from (
select nvl(b.income,0) as income, a.curDate as curDate, nvl(c.outlay,0) as outlay
from (
select to_char(sysdate-l_day,'DD.MM.YYYY') as curDate,
to_number(to_char(sysdate-l_day,'yyyymmdd')) as range_period
from (
select column_value as l_day
from table(sys.odcinumberlist(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))
)
order by l_day desc
) a
left join (
select to_number(to_char(created_date,'yyyymmdd')) as range_period,
sum(summa) as income
from factory_plats
where status=1 and factories_id = #{factories_id}
and created_date between to_date (sysdate-20,'dd.mm.yyyy') and to_date (sysdate,'dd.mm.yyyy') + 1 - 1 /24/60/60
group by to_number(to_char(created_date,'yyyymmdd'))
) b on a.range_period = b.range_period
left join (
select to_number(to_char(ci.created_date,'yyyymmdd')) as range_period,
sum(summa) as outlay,
f.factories_id as factories_id
from card_inputs ci
left join filials f on ci.filials_id = f.id
where ci.status=1 and f.factories_id = #{factories_id}
and ci.created_date between to_date (sysdate-20,'dd.mm.yyyy') and to_date (sysdate,'dd.mm.yyyy') + 1 - 1 /24/60/60
group by to_number(to_char(ci.created_date,'yyyymmdd')), f.factories_id
) c on a.range_period = c.range_period
)
This query is working SQL Navigator, but when I call from frontend returns 0

nested for loop in oracle to find similarity optimize

I have two tables both has same value but bot are from different source.
Table 1
------------
ID Title
1 Introduction to Science
2 Introduction to C
3 Let is C
4 C
5 Java
Table 2
------------------------
ID Title
a Intro to Science
b Intro to C
c Let is C
d C
e Java
I want to compare all the title in table 1 with title in table 2 and find the similarity match.
I Have used the built-in function in orcale "UTL_MATCH.edit_distance_similarity (LS_Title, LSO_Title);"
Script:
DECLARE
LS_count NUMBER;
LSO_count NUMBER;
percentage NUMBER;
LS_Title VARCHAR2 (4000);
LSO_Title VARCHAR2 (4000);
LS_CPNT_ID VARCHAR2 (64);
LSO_CPNT_ID VARCHAR2 (64);
BEGIN
SELECT COUNT (*) INTO LS_count FROM tbl_zim_item;
SELECT COUNT (*) INTO LSO_count FROM tbl_zim_lso_item;
DBMS_OUTPUT.put_line ('value of a: ' || LS_count);
DBMS_OUTPUT.put_line ('value of a: ' || LSO_count);
FOR i IN 1 .. LS_count
LOOP
SELECT cpnt_title
INTO LS_Title
FROM tbl_zim_item
WHERE iden = i;
SELECT cpnt_id
INTO LS_CPNT_ID
FROM tbl_zim_item
WHERE iden = i;
FOR j IN 1 .. lso_count
LOOP
SELECT cpnt_title
INTO LSO_Title
FROM tbl_zim_lso_item
WHERE iden = j;
SELECT cpnt_id
INTO LSO_CPNT_ID
FROM tbl_zim_lso_item
WHERE iden = j;
percentage :=
UTL_MATCH.edit_distance_similarity (LS_Title, LSO_Title);
IF percentage > 50
THEN
INSERT INTO title_sim
VALUES (ls_cpnt_id,
ls_title,
lso_cpnt_id,
lso_title,
percentage);
END IF;
END LOOP;
END LOOP;
END;
This is running for more than 15 hours. Kindly provide a better solution.
Note : My table 1 has 20000 records and table 2 has 10000 records.
Unless I'm missing something, you don't need all of the looping and row-by-row lookups since SQL can do cross joins. Therefore my first try would be just:
insert into title_sim
( columns... )
select ls_cpnt_id
, ls_title
, lso_cpnt_id
, lso_title
, percentage
from ( select i.cpnt_id as ls_cpnt_id
, i.cpnt_title as ls_title
, li.cpnt_id as lso_cpnt_id
, li.cpnt_title as lso_title
, case -- Using Boneist's suggestion:
when i.cpnt_title = li.cpnt_title then 100
else utl_match.edit_distance_similarity(i.cpnt_title, li.cpnt_title)
end as percentage
from tbl_zim_item i
cross join tbl_zim_lso_item li )
where percentage > 50;
If there is much repetition in the titles, you might benefit from some scalar subquery caching by wrapping the utl_match.edit_distance_similarity function in a ( select ... from dual ).
If the titles are often exactly the same and assuming in those cases percentage should be 100%, you might avoid calling the function when the titles are an exact match:
begin
select count(*) into ls_count from tbl_zim_item;
select count(*) into lso_count from tbl_zim_lso_item;
dbms_output.put_line('tbl_zim_item contains ' || ls_count || ' rows.');
dbms_output.put_line('tbl_zim_lso_item contains ' || lso_count || ' rows.');
for r in (
select i.cpnt_id as ls_cpnt_id
, i.cpnt_title as ls_title
, li.cpnt_id as lso_cpnt_id
, li.cpnt_title as lso_title
, case
when i.cpnt_title = li.cpnt_title then 100 else 0
end as percentage
from tbl_zim_item i
cross join tbl_zim_lso_item li
)
loop
if r.percentage < 100 then
r.percentage := utl_match.edit_distance_similarity(r.ls_title, r.lso_title);
end if;
if r.percentage > 50 then
insert into title_sim (columns...)
values
( ls_cpnt_id
, ls_title
, lso_cpnt_id
, lso_title
, percentage );
end if;
end loop;
end;
Rather than looping through all the data, I'd merely join the two tables together, eg:
WITH t1 AS (SELECT 1 ID, 'Introduction to Science' title FROM dual UNION ALL
SELECT 2 ID, 'Introduction to C' title FROM dual UNION ALL
SELECT 3 ID, 'Let is C' title FROM dual UNION ALL
SELECT 4 ID, 'C' title FROM dual UNION ALL
SELECT 5 ID, 'Java' title FROM dual UNION ALL
SELECT 6 ID, 'Oracle for Newbies' title FROM dual),
t2 AS (SELECT 'a' ID, 'Intro to Science' title FROM dual UNION ALL
SELECT 'b' ID, 'Intro to C' title FROM dual UNION ALL
SELECT 'c' ID, 'Let is C' title FROM dual UNION ALL
SELECT 'd' ID, 'C' title FROM dual UNION ALL
SELECT 'e' ID, 'Java' title FROM dual UNION ALL
SELECT 'f' ID, 'PL/SQL rocks!' title FROM dual)
SELECT t1.title t1_title,
t2.title t2_title,
UTL_MATCH.edit_distance_similarity(t1.title, t2.title)
FROM t1
INNER JOIN t2 ON UTL_MATCH.edit_distance_similarity(t1.title, t2.title) > 50;
T1_TITLE T2_TITLE UTL_MATCH.EDIT_DISTANCE_SIMILA
----------------------- ---------------- ------------------------------
Introduction to Science Intro to Science 70
Introduction to C Intro to C 59
Let is C Let is C 100
C C 100
Java Java 100
By doing that, you can then reduce the whole thing to a single DML statement, something like:
INSERT INTO title_sim (t1_id,
t1_title,
t2_id,
t2_title,
percentage)
SELECT t1.id t1_id,
t1.title t1_title,
t2.id t2_id,
t2.title t2_title,
UTL_MATCH.edit_distance_similarity(t1.title, t2.title) percentage
FROM t1
INNER JOIN t2 ON UTL_MATCH.edit_distance_similarity(t1.title, t2.title) > 50;
which ought to be a good deal faster than your row-by-row attempt, particularly as you are unnecessarily selecting from each table twice.
As an aside, you know that you can select multiple columns into multiple variables in the same query, right?
So instead of having:
SELECT cpnt_title
INTO LS_Title
FROM tbl_zim_item
WHERE iden = i;
SELECT cpnt_id
INTO LS_CPNT_ID
FROM tbl_zim_item
WHERE iden = i;
you could instead do:
SELECT cpnt_title, cpnt_id
INTO LS_Title, LS_CPNT_ID
FROM tbl_zim_item
WHERE iden = i;
https://www.techonthenet.com/oracle/intersect.php
this will give you data which is similar in both queries
select title from table_1
intersect
select title from table_2

How do I create a conditional SQL query

I am trying to create an Oracle Sql query using IF/Else statements
IF EXISTS
(
SELECT *
FROM baninst1.an_employee_position
WHERE baninst1.an_employee_position.person_uid = 593791
AND baninst1.an_employee_position.position_end_date IS NULL) THEN
SELECT *
FROM baninst1.an_employee_position
WHERE baninst1.an_employee_position.person_uid = 593791
AND (
baninst1.an_employee_position.position_end_date IS NULL
OR baninst1.an_employee_position.position_end_date > SYSDATE)
AND baninst1.an_employee_position.effective_start_date <= SYSDATE;ELSE
SELECT *
FROM (
SELECT *
FROM baninst1.an_employee_position
WHERE baninst1.an_employee_position.person_uid = 593791 )
WHERE ROWNUM = 1;END IF;
However I receive an "Unknown Command" error when I run it. No more error information
This may provide what you are looking for:
SELECT a.*
FROM employee_position a
where person_uid = 593791
and (
(a.position_end_date is null)
or
(
a.position_end_date =
(select max(position_end_date)
from employee_position b
where b.person_uid = a.person_uid
and b.position_end_date is not null
)
)
)
Another way
SELECT a.*
FROM employee_position a
where person_uid = 593791
and (
nvl(a.position_end_date, trunc(sysdate+100)) >=
(select max(position_end_date)
from employee_position b
where b.person_uid = a.person_uid
and b.position_end_date is not null
)
)

Can you create nested WITH clauses for Common Table Expressions?

WITH y AS (
WITH x AS (
SELECT * FROM MyTable
)
SELECT * FROM x
)
SELECT * FROM y
Does something like this work? I tried it earlier but I couldn't get it to work.
While not strictly nested, you can use common table expressions to reuse previous queries in subsequent ones.
To do this, the form of the statement you are looking for would be
WITH x AS
(
SELECT * FROM MyTable
),
y AS
(
SELECT * FROM x
)
SELECT * FROM y
You can do the following, which is referred to as a recursive query:
WITH y
AS
(
SELECT x, y, z
FROM MyTable
WHERE [base_condition]
UNION ALL
SELECT x, y, z
FROM MyTable M
INNER JOIN y ON M.[some_other_condition] = y.[some_other_condition]
)
SELECT *
FROM y
You may not need this functionality. I've done the following just to organize my queries better:
WITH y
AS
(
SELECT *
FROM MyTable
WHERE [base_condition]
),
x
AS
(
SELECT *
FROM y
WHERE [something_else]
)
SELECT *
FROM x
With does not work embedded, but it does work consecutive
;WITH A AS(
...
),
B AS(
...
)
SELECT *
FROM A
UNION ALL
SELECT *
FROM B
EDIT
Fixed the syntax...
Also, have a look at the following example
SQLFiddle DEMO
These answers are pretty good, but as far as getting the items to order properly, you'd be better off looking at this article
http://dataeducation.com/dr-output-or-how-i-learned-to-stop-worrying-and-love-the-merge
Here's an example of his query.
WITH paths AS (
SELECT
EmployeeID,
CONVERT(VARCHAR(900), CONCAT('.', EmployeeID, '.')) AS FullPath
FROM EmployeeHierarchyWide
WHERE ManagerID IS NULL
UNION ALL
SELECT
ehw.EmployeeID,
CONVERT(VARCHAR(900), CONCAT(p.FullPath, ehw.EmployeeID, '.')) AS FullPath
FROM paths AS p
JOIN EmployeeHierarchyWide AS ehw ON ehw.ManagerID = p.EmployeeID
)
SELECT * FROM paths order by FullPath
we can create nested cte.please see the below cte in example
;with cte_data as
(
Select * from [HumanResources].[Department]
),cte_data1 as
(
Select * from [HumanResources].[Department]
)
select * from cte_data,cte_data1
I was trying to measure the time between events with the exception of what one entry that has multiple processes between the start and end. I needed this in the context of other single line processes.
I used a select with an inner join as my select statement within the Nth cte. The second cte I needed to extract the start date on X and end date on Y and used 1 as an id value to left join to put them on a single line.
Works for me, hope this helps.
cte_extract
as
(
select ps.Process as ProcessEvent
, ps.ProcessStartDate
, ps.ProcessEndDate
-- select strt.*
from dbo.tbl_some_table ps
inner join (select max(ProcessStatusId) ProcessStatusId
from dbo.tbl_some_table
where Process = 'some_extract_tbl'
and convert(varchar(10), ProcessStartDate, 112) < '29991231'
) strt on strt.ProcessStatusId = ps.ProcessStatusID
),
cte_rls
as
(
select 'Sample' as ProcessEvent,
x.ProcessStartDate, y.ProcessEndDate from (
select 1 as Id, ps.Process as ProcessEvent
, ps.ProcessStartDate
, ps.ProcessEndDate
-- select strt.*
from dbo.tbl_some_table ps
inner join (select max(ProcessStatusId) ProcessStatusId
from dbo.tbl_some_table
where Process = 'XX Prcss'
and convert(varchar(10), ProcessStartDate, 112) < '29991231'
) strt on strt.ProcessStatusId = ps.ProcessStatusID
) x
left join (
select 1 as Id, ps.Process as ProcessEvent
, ps.ProcessStartDate
, ps.ProcessEndDate
-- select strt.*
from dbo.tbl_some_table ps
inner join (select max(ProcessStatusId) ProcessStatusId
from dbo.tbl_some_table
where Process = 'YY Prcss Cmpltd'
and convert(varchar(10), ProcessEndDate, 112) < '29991231'
) enddt on enddt.ProcessStatusId = ps.ProcessStatusID
) y on y.Id = x.Id
),
.... other ctes
Nested 'With' is not supported, but you can always use the second With as a subquery, for example:
WITH A AS (
--WITH B AS ( SELECT COUNT(1) AS _CT FROM C ) SELECT CASE _CT WHEN 1 THEN 1 ELSE 0 END FROM B --doesn't work
SELECT CASE WHEN count = 1 THEN 1 ELSE 0 END AS CT FROM (SELECT COUNT(1) AS count FROM dual)
union all
select 100 AS CT from dual
)
select CT FROM A