Query optimization - avoid full outer join - join

below is the data scripts used.
create table enab_test (ban varchar2(10),DVR_IND char(1));
create table oms_test (ban varchar2(10),DVR_IND char(1));
insert into enab_test values('111111111','Y');
insert into enab_test values('123456789','N');
insert into enab_test values('321654987','N');
insert into enab_test values('444444444','Y');
insert into enab_test values('111111111','Y');
insert into enab_test values('741258963','Y');
insert into oms_test values('111111111','Y');
insert into oms_test values('222222222','N');
insert into oms_test values('333333333','N');
insert into oms_test values('444444444','N');
insert into oms_test values('555555555','Y');
insert into oms_test values('666666666','N');
Here i'm trying to get the count of bans present in enab_test table where if the ban having dvr_ind = 'Y' then it is counted as one and if there are many with same ban then it is summed up. if there are same ban in Oms with dvr_ind = 'Y'. the count is subracted like enab_test - oms_test for bans. if we have any negative count while subracting from enab_test - oms_test then the count is made zero. and viceversa(oms_test-enab_test).
below is the query I worked on.
select nvl(enab.ENAB_COUNT,0) as ENAB_COUNT,
case when nvl(enab.ENAB_COUNT,0) - nvl(oms.OMS_COUNT,0) > 0
then nvl(enab.ENAB_COUNT,0) - nvl(oms.OMS_COUNT,0)
else 0
end as IN_ENAB_NOT_OMS
from (
select ban, count(*) as OMS_COUNT from oms_test
where dvr_ind = 'Y'
group by ban order by ban
) oms
full outer join (
select ban, count(*) as ENAB_COUNT from enab_test
where dvr_ind = 'Y'
group by ban order by ban
) enab
on oms.BAN = enab.BAN
Below is the output of the query
ENAB_COUNT IN_ENAB_NOT_OMS
2 1
0 0
1 1
1 1
Run Plan For the above query with original data is
Plan hash value: 2599843784
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6705K| 166M| | 10M (9)| 33:33:17 |
| 1 | VIEW | VW_FOJ_0 | 6705K| 166M| | 10M (9)| 33:33:17 |
|* 2 | HASH JOIN FULL OUTER| | 6705K| 255M| 204M| 10M (9)| 33:33:17 |
| 3 | VIEW | | 6703K| 127M| | 4767K (9)| 15:53:31 |
| 4 | SORT GROUP BY | | 6703K| 76M| 14G| 4767K (9)| 15:53:31 |
|* 5 | TABLE ACCESS FULL| OMS_TEST | 764M| 8749M| | 3335K (7)| 11:07:08 |
| 6 | VIEW | | 6705K| 127M| | 5278K (9)| 17:35:37 |
| 7 | SORT GROUP BY | | 6705K| 76M| 15G| 5278K (9)| 17:35:37 |
|* 8 | TABLE ACCESS FULL| ENAB_TEST | 805M| 9215M| | 3770K (7)| 12:34:05 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OMS"."BAN"="ENAB"."BAN")
5 - filter("DVR_IND"='Y')
8 - filter("DVR_IND"='Y')
I have created the above query and works fine on smaller data. its taking too much time if i'm having lot of records.Is there any way to remove full outer join and optimize the query or any other ways to optimize ?

Related

Firebird stored procedure with indexed variable using execute statement

How can I use indexed variable in a Firebird stored procedure? I mean, I have output parameters ODATE1, ODATE2, ODATE3, can I use as ':ODATE' || COUNTER to set the value in a loop?
I have 2 tables like this:
1. T_EMPLOYEE
---------------
| ID_E | NAME |
---------------
| 1 | Anne |
---------------
| 2 | Bob |
---------------
2. T_WORK
----------------------------
| ID_W | DATE_W | ID_E |
----------------------------
| 1 | 2021-01-01 | 1 |
----------------------------
| 2 | 2021-01-01 | 2 |
----------------------------
| 3 | 2021-01-02 | 1 |
----------------------------
| 4 | 2021-01-03 | 2 |
----------------------------
From that table I want to make a stored procedure to get this result:
DASHBOARD
-----------------------------------------------------------
| OID_E | ONAME | ODATE1 | ODATE2 | ODATE3 |
----------------------------------------------------------
| 1 | Anne | 1 | 1 | 0 |
-----------------------------------------------------------
| 2 | Bob | 1 | 0 | 1 |
-----------------------------------------------------------
I tried using EXECUTE STATEMENT like this in stored procedure:
DECLARE VARIABLE COUNTER INT;
BEGIN
FOR
SELECT ID_E, NAME FROM T_EMPLOYEE
INTO :OID_E, :ONAME
D0
BEGIN
COUNTER = 1;
WHILE (COUNTER<=3) DO
BEGIN
EXECUTE STATEMENT 'SELECT COUNT(*) FROM T_WORK WHERE DATE_W = ''2021-01-0' || COUNTER ||
''' AND ID_E = ' || :OID_E || ' INTO :ODATE' || COUNTER;
COUNTER = COUNTER + 1;
END
SUSPEND;
END
END /*procedure*/
The procedure can't be compiled.
Then I tried the simple one like this without COUNTER index replacement:
DECLARE VARIABLE COUNTER INT;
BEGIN
FOR
SELECT ID_E, NAME FROM T_EMPLOYEE
INTO :OID_E, :ONAME
D0
BEGIN
COUNTER = 1;
WHILE (COUNTER<=3) DO
BEGIN
EXECUTE STATEMENT 'SELECT COUNT(*) FROM T_WORK WHERE ID_E = :OID_E ' ||
' AND DATE_W =''2021-01-02'' INTO :ODATE2';
COUNTER = COUNTER + 1;
END
SUSPEND;
END
END /*procedure*/
The procedure can be compiled, but when I execute, it will raise this error:
SQL Error: Dynamic SQL Error SQL error code = #1 Token unknown - line #1, column #2 #1. Error Code: -104. Invalid token
Please give me insight. How to use EXECUTE STATEMENT to make a flexible looping to set indexed variable.
Or you have another solution for my needs.
Additional information: Firebird v2.5
You cannot dynamically reference PSQL variables (including parameters) like this. However, you don't need to jump through all these hoops to get the desired results.
You can use something like the following(which doesn't even need a procedure):
select e.ID_E as OID_E, e.NAME as ONAME,
count(case when w.DATE_W = date '2021-01-01' then 1 end) as ODATE1,
count(case when w.DATE_W = date '2021-01-02' then 1 end) as ODATE2,
count(case when w.DATE_W = date '2021-01-03' then 1 end) as ODATE3
from T_EMPLOYEE e
inner join T_WORK w
on w.ID_E = e.ID_E
group by e.ID_E, e.NAME
order by e.ID_E
Fiddle: https://dbfiddle.uk/?rdbms=firebird_3.0&fiddle=59066afc0fd7f6b5cb87eac99164e899

Not null query extremely slow in ajax request partial

I have this simple scope
scope :has_video_link,
-> { where.not(video_link: nil) }
I use it like so in my controller
#videos = Message .featured
.includes(:user, :company, :forum, :topic)
.published
.unremoved
.approved
.has_video_link
.order(created_at: :desc)
.paginate(
page: page,
per_page: limit)
Which I call like so
before_action only: :index
skip_before_action :verify_authenticity_token
def index
self.page_title = 'Home'
fetch_videos
end
That works just fine, and my page loads in under a second on prod no problem. My issue is I have an ajax request that calls this
def videos
fetch_videos
respond_to do |format|
format.js
end
end
On dev this works great but in prod this takes over 60 seconds....
First image is from the first times its called (4ms)
2nd image is the ajax call 69000ms...
Any ideas? How do i fix this?? Thank you!!
edit: here is the explain
EXPLAIN for: SELECT `messages`.* FROM `messages` WHERE `messages`.`is_featured` = 1 AND (messages.created_at<='2021-06-04 12:36:22.241601') AND `messages`.`is_removed` = 0 AND `messages`.`is_approved` = 1 AND (`messages`.`video_link` IS NOT NULL) ORDER BY `messages`.`created_at` DESC LIMIT 5 OFFSET 0
+----+-------------+----------+------------+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+------------+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | messages | NULL | range | index_messages_on_is_removed_and_user_id_and_created_at,index_messages_on_is_removed_and_message_type_and_created_at,index_messages_on_is_removed_company_id_type_created_at,index_messages_on_is_removed_company_id_created_at_id_user_id,index_messages_on_is_removed_and_created_at,index_messages_on_is_removed_company_id_created_at_rating_count,index_messages_on_is_removed_company_id_user_role_created_at,index_messages_on_is_removed_and_company_id_and_rating_total,index_messages_on_is_removed_and_forum_id_and_created_at,index_messages_on_is_approved,index_messages_on_is_removed_and_is_approved | index_messages_on_is_removed_and_created_at | 7 | NULL | 3470 | 9.0 | Using index condition; Using where |
+----+-------------+----------+------------+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------+---------+------+------+----------+------------------------------------+
1 row in set (0.00 sec)
EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 594568
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | users | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.0 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set (0.00 sec)
EXPLAIN for: SELECT `companies`.* FROM `companies` WHERE `companies`.`id` IN (564013, 562440)
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | companies | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 2 | 100.0 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set (0.01 sec)
EXPLAIN for: SELECT `forums`.* FROM `forums` WHERE `forums`.`id` IN (12224, 7759)
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | forums | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 2 | 100.0 | Using where |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set (0.00 sec)
EXPLAIN for: SELECT `topics`.* FROM `topics` WHERE `topics`.`id` IN (684474, 684473, 684472, 684470, 684467)
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | topics | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 5 | 100.0 | Using where |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set (0.00 sec)
Solved this issue. All I had to do was simply index video_links and that fixed my issue right up!

Combine rows in a table using the a column as reference [PSQL]

I have a postgresql table like this:
| id | date | status |
|------|------------|--------|
| id1 | 2019-01-01 | ON |
| id1 | 2019-01-02 | OFF |
| id1 | 2019-01-03 | ON |
How can I combine the rows into sth like this:
| id | on_date | off_date |
|------|------------|------------|
| id1 | 2019-01-01 | 2019-01-02 |
| id1 | 2019-01-03 | |
OFF status always inserted after ON status (there will be a on_date before off_date)
My current solution is:
WITH ons AS (
SELECT id, row_number() OVER(ORDER BY date) as row_num, date as on_date
FROM table
WHERE status = 'ON' AND id = id1),
offs AS (
SELECT id, row_number() OVER(ORDER BY date) as row_num, date as off_date
FROM table
WHERE status = 'OFF' AND id = id1)
SELECT ons.id, ons.on_date, offs.off_date
FROM ons
LEFT JOIN offs ON ons.id = off.id AND ons.row_num = offs.row_num
Can I do this with simpler query? Thanks.
use below SQL :
select tt.id, tt.date as on_date, (select idate from
table where id=tt.id and status='OFF' and
date>=tt.idate and rownum<2) as off_date from table tt
where status='ON'
I think you get the result using below query:
SELECT id,on_date,off_date from (SELECT id,date as on_date,'' as off_date FROM test WHERE status = 'ON' UNION ALL SELECT id,'' as on_date,date as off_date FROM test WHERE status = 'OFF') as a

Query for PSQL determine no.of patients in system current time

select room_id,count (distinct(patient_id)) as patient_id
from patient_flow where time='CURRENT_TIME';
I need the number of patients ids with corresponding room_ids at the current time.
id | patient_id | room_id | time
----+-------------+---------+---------------------
1 | 00035-67351 | 1 | 2015-06-09 10:11:20
1 | 00035-67351 | 2 | 2015-06-09 10:31:20
1 | 00035-67351 | 1 | 2015-06-09 10:12:20
1 | 00035-67351 | 1 | 2015-06-09 10:12:40
1 | 00035-67351 | 1 | 2015-06-09 10:15:40
1 | 00035-67351 | 1 | 2015-06-09 10:30:40
1 | 00035-67351 | 2 | 2015-06-09 10:32:40
1 | 00035-67351 | 2 | 2015-06-09 10:36:40
1 | 00035-67351 | 2 | 2015-06-09 10:38:40
1 | 00035-67351 | 2 | 2015-06-09 10:50:40
I wrote the query above, but it does not execute.
You missed the GROUP BY when using with aggregate function COUNT(). The below query will work:
SELECT room_id, COUNT (DISTINCT(patient_id)) AS patient_id
FROM patient_flow
WHERE time = CURRENT_DATE
GROUP BY room_id;
select current_time, room_id, count (distinct(patient_id)) as patient_id from patient_flow group by room_id;
I got result while executing above query..

Grouping in MDX Query

I am very newbie to MDX world..
I want to group the Columns based on only 3 rows. But, need join for 4th row also..
My query is :
SELECT
( {
[Measures].[Live Item Count]
}
) DIMENSION PROPERTIES parent_unique_name ON COLUMNS,
Crossjoin(
crossjoin(
[Item].[Class].&[Light],
[Item].[Style].&[Fav]
[Item].[Season Year].members),
[Item].[Count].children ) on rows
FROM Cube
Output comes as :
Light(Row) | FAV(Row) | ALL(Row) | 16(Row) | 2(col)
Light(Row) | FAV(Row) | ALL(Row) | 7(Row) | 1(col)
Light(Row) | FAV(Row) | 2012(Row) | 16(Row)| 2(col)
Light(Row) | FAV(Row) | 2011(Row) | 7(Row) | 1(col)
But, I want my output to be displayed as:
Light(Row) | FAV(Row) | ALL(Row) | | 3(col)
Light(Row) | FAV(Row) | 2012(Row) | 16(Row)| 2(col)
Light(Row) | FAV(Row) | 2011(Row) | 7(Row) | 1(col)
i.e., I want to group my first two rows such that there is no duplicate 'ALL' in 3rd column..
Thanks in advance
Try this - using the level name Season Year with the Attribute name Season Year will pick up every member without teh ALL member:
SELECT
( {
[Measures].[Live Item Count]
}
) DIMENSION PROPERTIES parent_unique_name ON COLUMNS,
Crossjoin(
crossjoin(
[Item].[Class].&[Light],
[Item].[Style].&[Fav]
[Item].[Season Year].[Season Year].members),
[Item].[Count].children ) on rows
FROM Cube
You can use this query if there is an All member on the [Item].[Count] hierarchy:
SELECT {[Measures].[Live Item Count]} DIMENSION PROPERTIES parent_unique_name ON COLUMNS,
Crossjoin(
Crossjoin([Item].[Class].&[Light], [Item].[Style].&[Fav]),
Union(
Crossjoin({"All member of [Item].[Season Year]"}, {"All member of [Item].[Count]"}),
Crossjoin(Except([Item].[Season Year].members, {"All member of [Item].[Season Year]"}), [Item].[Count].children),
) ON ROWS
FROM Cube

Resources