I am trying to create an SQL query to select percentage utilization for my team members.
I am looking for a to daily basis select query to show the utilization when it is greater than 0 and to show '0' when it is null. In order to be able to plot those figures later on as a line chart.
I have written 365 of the following select query and I have combined them together using "UNION ALL" method. It is working now, but it is extremely slow!.
If you have a better idea to produce the daily outputs for the 365 days, that will be great.
Many thanks in advance.
Kind regards
Mohamed
Code:
.........................
SELECT ifnull((
SELECT SUM(CAST(pc_loading.value AS INT))
FROM ticket
JOIN ticket_custom start_date ON (ticket.id = start_date.ticket AND start_date.name = 'planned_start_date')
JOIN ticket_custom finish_date ON (ticket.id = finish_date.ticket AND finish_date.name = 'planned_finish_date')
JOIN ticket_custom pc_loading ON (ticket.id = pc_loading.ticket AND pc_loading.name = 'percentage_loading')
WHERE UPPER(ticket.owner)='#########'
AND ticket.type = 'Internal Resource'
AND DATE(substr(start_date.value, 7, 4) || '-' || substr(start_date.value, 4, 2) || '-' || substr(start_date.value, 1, 2)) <= DATE('2020-01-01')
AND DATE(substr(finish_date.value, 7, 4) || '-' || substr(finish_date.value, 4, 2) || '-' || substr(finish_date.value, 1, 2)) >= DATE('2020-01-01')
AND status NOT IN ("closed", "suspended")) , 0) UNION ALL
SELECT ifnull((
SELECT SUM(CAST(pc_loading.value AS INT))
FROM ticket
JOIN ticket_custom start_date ON (ticket.id = start_date.ticket AND start_date.name = 'planned_start_date')
JOIN ticket_custom finish_date ON (ticket.id = finish_date.ticket AND finish_date.name = 'planned_finish_date')
JOIN ticket_custom pc_loading ON (ticket.id = pc_loading.ticket AND pc_loading.name = 'percentage_loading')
WHERE UPPER(ticket.owner)='#########'
AND ticket.type = 'Internal Resource'
AND DATE(substr(start_date.value, 7, 4) || '-' || substr(start_date.value, 4, 2) || '-' || substr(start_date.value, 1, 2)) <= DATE('2020-01-02')
AND DATE(substr(finish_date.value, 7, 4) || '-' || substr(finish_date.value, 4, 2) || '-' || substr(finish_date.value, 1, 2)) >= DATE('2020-01-02')
AND status NOT IN ("closed", "suspended")) , 0) UNION ALL
.......etc
....................................
Use a recursive CTE which returns all the dates of 2020, cross join it to your query and group by each date:
with recursive days as (
select date('2020-01-01') date
union all
select date(date, '+1 day')
from days
where strftime('%Y', date, '+1 day') = '2020'
)
SELECT days.date, COALESCE(SUM(CAST(pc_loading.value AS INT)), 0)
FROM days
CROSS JOIN ticket
JOIN ticket_custom start_date ON (ticket.id = start_date.ticket AND start_date.name = 'planned_start_date')
JOIN ticket_custom finish_date ON (ticket.id = finish_date.ticket AND finish_date.name = 'planned_finish_date')
JOIN ticket_custom pc_loading ON (ticket.id = pc_loading.ticket AND pc_loading.name = 'percentage_loading')
WHERE UPPER(ticket.owner)='#########' AND ticket.type = 'Internal Resource'
AND DATE(substr(start_date.value, 7, 4) || '-' || substr(start_date.value, 4, 2) || '-' || substr(start_date.value, 1, 2)) <= days.date
AND DATE(substr(finish_date.value, 7, 4) || '-' || substr(finish_date.value, 4, 2) || '-' || substr(finish_date.value, 1, 2)) >= days.date
AND status NOT IN ("closed", "suspended")
GROUP BY days.date
Also, you would find significant improvement in performance if you stored the dates in a comparable format like YYYY-MM-DD since there would be no need for all these rearrangements/concatenations in the WHERE clause.
Alternative without the use of the CTE:
SELECT days.date, COALESCE(SUM(CAST(pc_loading.value AS INT)), 0)
FROM (
SELECT '2020-01-01' date UNION ALL SELECT '2020-01-02' UNION ALL
SELECT '2020-01-03' UNION ALL SELECT '2020-01-04' UNION ALL
............................................................
) days
CROSS JOIN ticket
JOIN ticket_custom start_date ON (ticket.id = start_date.ticket AND start_date.name = 'planned_start_date')
JOIN ticket_custom finish_date ON (ticket.id = finish_date.ticket AND finish_date.name = 'planned_finish_date')
JOIN ticket_custom pc_loading ON (ticket.id = pc_loading.ticket AND pc_loading.name = 'percentage_loading')
WHERE UPPER(ticket.owner)='#########' AND ticket.type = 'Internal Resource'
AND DATE(substr(start_date.value, 7, 4) || '-' || substr(start_date.value, 4, 2) || '-' || substr(start_date.value, 1, 2)) <= days.date
AND DATE(substr(finish_date.value, 7, 4) || '-' || substr(finish_date.value, 4, 2) || '-' || substr(finish_date.value, 1, 2)) >= days.date
AND status NOT IN ("closed", "suspended")
GROUP BY days.date
Related
I need to refactor my query, but I have no idea, how to do this.
I can see several duplicates of using the same logic, but I continue to repeat this manner of querying from query to query, and I feel, that this kind of querying becomes my main frame of thinking of SQL and I don't want this.
Could you show me more acceptable variant of this query, so I won't repeat my way of thinking again?
Here it is
WITH fourth_table AS
(
WITH third_table AS
(
WITH second_table AS
(
WITH initial_table AS
(
SELECT
DISTINCT ctr.country_region, EXTRACT (YEAR FROM s.time_id)::int AS calendar_year, chn.channel_desc,
SUM(s.amount_sold) OVER (PARTITION BY ctr.country_region||chn.channel_desc||EXTRACT (YEAR FROM s.time_id)) AS amount_sold
FROM sales s
JOIN channels chn ON s.channel_id = chn.channel_id
JOIN customers c ON s.cust_id = c.cust_id
JOIN countries ctr ON c.country_id = ctr.country_id
WHERE ctr.country_region IN ('Americas','Asia', 'Europe')
AND
EXTRACT (YEAR FROM s.time_id)::int IN (1998, 1999, 2000, 2001)
ORDER BY ctr.country_region, calendar_year, chn.channel_desc
)
SELECT country_region, calendar_year, channel_desc, amount_sold,
(amount_sold/SUM(amount_sold) OVER (PARTITION BY country_region||calendar_year)*100)::decimal(10,2) AS bychannels
FROM initial_table
)
SELECT *,
LAG (bychannels, 4) OVER (ORDER BY 6) AS lower_salary
FROM second_table--correct here smth wrong
)
SELECT *, bychannels - lower_salary AS diff FROM third_table
)
SELECT country_region, calendar_year, channel_desc,
--'FM 999,999,999,990D'
LPAD(to_char(amount_sold, 'FM999,999,999,990 $'),20, ' ') AS amount_sold,
LPAD(bychannels || ' %' ,20, ' ') AS "% BY CHANNELS",
LPAD(lower_salary || ' % ' ,20, ' ') AS "% PREVIOUS PERIOD",
diff AS "% DIFF"
FROM fourth_table WHERE calendar_year NOT IN (1998);
You are mixing CTE (Common Table Queries) with Subqueries, the beauty of the with clause is normally the readability:
with initial_table as
(
SELECT
DISTINCT ctr.country_region, EXTRACT (YEAR FROM s.time_id)::int AS calendar_year, chn.channel_desc,
SUM(s.amount_sold) OVER (PARTITION BY ctr.country_region||chn.channel_desc||EXTRACT (YEAR FROM s.time_id)) AS amount_sold
FROM sales s
JOIN channels chn ON s.channel_id = chn.channel_id
JOIN customers c ON s.cust_id = c.cust_id
JOIN countries ctr ON c.country_id = ctr.country_id
WHERE ctr.country_region IN ('Americas','Asia', 'Europe')
AND
EXTRACT (YEAR FROM s.time_id)::int IN (1998, 1999, 2000, 2001)
ORDER BY ctr.country_region, calendar_year, chn.channel_desc
)
,second_table as
(
SELECT country_region, calendar_year, channel_desc, amount_sold,
(amount_sold/SUM(amount_sold) OVER (PARTITION BY country_region||calendar_year)*100)::decimal(10,2) AS bychannels
FROM initial_table
)
,third_table as
(
SELECT *,
LAG (bychannels, 4) OVER (ORDER BY 6) AS lower_salary
FROM second_table--correct here smth wrong
)
,fourth_table as
(
SELECT *, bychannels - lower_salary AS diff FROM third_table
)
SELECT country_region, calendar_year, channel_desc,
--'FM 999,999,999,990D'
LPAD(to_char(amount_sold, 'FM999,999,999,990 $'),20, ' ') AS amount_sold,
LPAD(bychannels || ' %' ,20, ' ') AS "% BY CHANNELS",
LPAD(lower_salary || ' % ' ,20, ' ') AS "% PREVIOUS PERIOD",
diff AS "% DIFF"
FROM fourth_table WHERE calendar_year NOT IN (1998);
I am a little bit confusing and have no idea how to solve this problem. I have column in table Quantity which store Time value.
I want to create a following story. If I have for example
Quantity
8:00
8:00
It needs to SUM() this two and in output I need to get 16 HOURS
Second think, it needs to take last two number :00 and add to HOURS.
This is what I do so far
SELECT
(SUM(SUBSTR(A.Quantity, ':', 1)) + TRUNC((SUM(SUBSTR(A.Quantity, ':', -1)) / 60),0)), ':' ,
MOD(SUM(SUBSTR(A.Quantity, ':' , -1)), 60)
AS TOTAL_SUM FROM (
SELECT
ata.ATAID AS AtaId, ata.ProjectID, ata.StartDate, ataAW.Quantity
FROM
ata
INNER JOIN
weekly_report
ON
weekly_report.ataId = ata.ATAID
INNER JOIN
ata_articles ataAW
ON
ataAW.wrId = weekly_report.id
WHERE
ata.ATAID = 10987
AND
ataAW.type = 1
OR
ataAW.type = 2
OR
ataAW.type = 3
AND
(weekly_report.status != 3 AND weekly_report.status != 4)
AND
(
weekly_report.year < (SELECT year FROM weekly_report WHERE id = 89)
OR
(
weekly_report.year <= (SELECT year FROM weekly_report WHERE id = 89)
AND
weekly_report.week <= (SELECT week FROM weekly_report WHERE id = 89)
)
)
) A
group by A.AtaId
So far better explanation, when I run first part of query I need to get something like
SELECT
CONCAT(
-- extract hours froAm time and add minutes converted to hours
(SUM(SUBSTRING_INDEX(aa.Quantity, ':', 1)) + TRUNCATE((SUM(SUBSTRING_INDEX(aa.Quantity, ':', -1)) / 60),0))
-- , ':',
-- extract minutes from time and find reminder (modulo)*/
-- LPAD((SUM(SUBSTRING_INDEX(aa.Quantity, ':', -1)) % 60), 2, 0)
) AS W_TOTAL_SUM
FROM
ata_articles aa
INNER JOIN
weekly_report wr
ON
aa.wrId = wr.id
WHERE
aa.wrId = 69
AND
aa.type = 1
TOTAL_SUM
16
And when I run second part
SELECT
CONCAT(
-- extract hours froAm time and add minutes converted to hours
-- (SUM(SUBSTRING_INDEX(aa.Quantity, ':', 1)) + TRUNCATE((SUM(SUBSTRING_INDEX(aa.Quantity, ':', -1)) / 60),0))
-- , ':',
-- extract minutes from time and find reminder (modulo)*/
LPAD((SUM(SUBSTRING_INDEX(aa.Quantity, ':', -1)) % 60), 2, 0)
) AS W_TOTAL_SUM
FROM
ata_articles aa
INNER JOIN
weekly_report wr
ON
aa.wrId = wr.id
WHERE
aa.wrId = 69
AND
aa.type = 1
I get output
TOTAL_SUM
00
Can someone guide me and tell me how to solve this issue since I try every solution but unfortunetlly doesn't work. And here is what I try so far, but I always get message
ORA-01722: invalid number
01722. 00000 - "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number
SELECT
(SUM(SUBSTR(A.Quantity, ':', 1)) + TRUNC((SUM(SUBSTR(A.Quantity, ':', -1)) / 60),0)), ':' ,
MOD(SUM(SUBSTR(A.Quantity, ':' , -1)), 60)
AS TOTAL_SUM FROM (
SELECT
ata.ATAID AS AtaId, ata.ProjectID, ata.StartDate, ataAW.Quantity
FROM
ata
INNER JOIN
weekly_report
ON
weekly_report.ataId = ata.ATAID
INNER JOIN
ata_articles ataAW
ON
ataAW.wrId = weekly_report.id
WHERE
ata.ATAID = 10987
AND
ataAW.type = 1
OR
ataAW.type = 2
OR
ataAW.type = 3
AND
(weekly_report.status != 3 AND weekly_report.status != 4)
AND
(
weekly_report.year < (SELECT year FROM weekly_report WHERE id = 89)
OR
(
weekly_report.year <= (SELECT year FROM weekly_report WHERE id = 89)
AND
weekly_report.week <= (SELECT week FROM weekly_report WHERE id = 89)
)
)
) A
group by A.AtaId
UPDATE
I get output error message
ORA-00907: missing right parenthesis
00907. 00000 - "missing right parenthesis"
*Cause:
*Action:
Error at Line: 267 Column: 19
SELECT ( EXTRACT( DAY FROM duration ) * 24 + EXTRACT( HOUR FROM duration ) )
|| ':'
|| TO_CHAR( EXTRACT( MINUTE FROM DURATION ), 'FM00' )
|| ' HOURS' AS duration
FROM (
SELECT NUMTODSINTERVAL(SUM( SUBSTR( quantity, 1, INSTR( quantity, ':' ) - 1 ) ),'HOUR')
+ NUMTODSINTERVAL(SUM( SUBSTR( quantity, INSTR( quantity, ':' ) + 1 ) ), 'MINUTE' )
AS duration
FROM (
SELECT ata.ATAID AS AtaId, ata.ProjectID, ata.StartDate, ataAW.Quantity
FROM ata
INNER JOIN weekly_report
ON weekly_report.ataId = ata.ATAID
INNER JOIN ata_articles ataAW
ON ataAW.wrId = weekly_report.id
INNER JOIN (SELECT week, year FROM weekly_report WHERE id = 89 ) b
ON ( weekly_report.year < b.year OR ( weekly_report.year = b.year AND weekly_report.week <= b.week ))
WHERE ata.ATAID = 10987
AND ataAW.type IN ( 1, 2, 3 )
AND weekly_report.status NOT IN ( 3, 4 )
))
group by A.AtaId
Here is what I get as output when I execute following code
DURATION
:HOURS
If you have the (slightly more complicated) sample data:
CREATE TABLE table_name ( Quantity ) AS
SELECT '8:00' FROM DUAL UNION ALL
SELECT '7:30' FROM DUAL UNION ALL
SELECT '0:30' FROM DUAL;
Then you can use string functions to get the hour and minute parts and sum those and then convert the totals to an interval (so you don't end up with 15:60 HOURS) and then format the output:
SELECT ( EXTRACT( DAY FROM duration ) * 24
+ EXTRACT( HOUR FROM duration )
)
|| ':'
|| TO_CHAR( EXTRACT( MINUTE FROM DURATION ), 'FM00' )
|| ' HOURS' AS duration
FROM (
SELECT NUMTODSINTERVAL(
SUM( SUBSTR( quantity, 1, INSTR( quantity, ':' ) - 1 ) ),
'HOUR'
)
+
NUMTODSINTERVAL(
SUM( SUBSTR( quantity, INSTR( quantity, ':' ) + 1 ) ),
'MINUTE'
) AS duration
FROM table_name
);
Which outputs:
| DURATION |
| :---------- |
| 16:00 HOURS |
db<>fiddle here
I Have Query
SELECT * FROM PPI_CD WHERE PATIENT_NO = 14683
Results from this query display some of the same data based on PATIENT_NO
How to order only the latest data out? only One
Full Query
SELECT
PPI_ID,
co.order_no,
p.RM_NO,
co.patient_name,
EXTRACT(YEAR FROM SYSDATE) - EXTRACT(YEAR FROM p.DOB) as Age,
p.sex,
co.order_date as TGL_MASUK,
NVL(il.NAME, '0') as ICD,
NVL(ppi.HPA_UC, '0') AS UC,
NVL(ppi.HPA_IVL, '0') AS IVL,
NVL(ppi.HPA_CVL, '0') AS CVL,
NVL(ppi.HPA_ETT, '0') AS ETT,
NVL(ppi.IRS_VAP, '0') AS VAP,
NVL(ppi.IRS_PLEB, '0') AS PLEB,
NVL(ppi.IRS_ISK, '0') AS ISK,
NVL(ppi.IRS_IAD, '0') AS IAD,
NVL(ppi.TB, '0') AS TB,
NVL(ppi.HK, '0') AS HK,
NVL(ppi.AB, '0') AS AB,
NVL(ppi.ANTIBIOTIK , '0') AS ANTIBIOTIK,
NVL(ppi.DEKU, '0') AS DEKU,
NVL(ppi.JK_DARAH , '0') AS DARAH,
NVL(ppi.JK_SWAB , '0') AS SWAB,
NVL(ppi.JK_SPUTUM , '0') AS SPUTUM,
NVL(ppi.JK_URINE , '0') AS URINE,
NVL(ppi.TEMP, 'N/A') AS TEMP
FROM case_orders co
LEFT JOIN PPI_CD cd
ON cd.patient_no = co.patient_no
LEFT JOIN illness_lists il
ON il.illness_no = cd.illness_no
LEFT JOIN patients p
ON p.contact_no = co.patient_no
LEFT JOIN PPI ppi
ON ppi.RM_NO = p.RM_NO
WHERE CO.status_no=5
ORDER BY CO.ORDER_DATE ASC;
the problem is with this
out 2 data which should be only one data
For just one patient, you can order by and a row limiting clause:
select *
from ppi_cd
where patient_no = 14683
order by posted_date desc
fetch first row only
If you want the latest record per patient_no, you can do:
select c.*
from ppi_cd c
where c.posted_date = (
select max(c1.posted_date) from ppi_cd c1 where c1.patient_no = c.patient_no
)
Use row_number() :
select pc.*
from (select pc.*, row_number() over (partition by PATIENT_NO order by pc.post_date DESC) as seq
from PPI_CD pc
) pc
where pc.seq = 1;
select
d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME || ', ' || c.PTFIRSTNAME as "Patient name",
min(b.created) as Data Entry 1,
max(b.created) as Data Entry 2,
min(b.created) - max(b.created)
|| ' days and '
|| TO_CHAR(to_date('01/01/2000', 'MM-DD-YYYY')
+ (b.created - b.created), 'HH24:MI:SS' ) AS Diff
FROM
CR_MDT a
full outer join CR_MDT_VERIFY b
on a.CR_MDT_ID = b.FK_CR_MDT_ID
left join patient c
on a.FK_SOCRD_ID = c.SOCRD_ID
left join PT_STUDY d
on c.SOCRD_ID = d.FK_SOCRD_ID
where
a.CR_MDT_DT between TO_DATE('01/01/2017', 'mm/dd/yyyy')
and TO_DATE('12/31/2017', 'mm/dd/yyyy')
group by
d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME,
c.PTFIRSTNAME,
b.created
How can I subtract the max(b.created) from min(b.created)? I'm stuck on this part:
|| TO_CHAR(to_date('01/01/2000', 'MM-DD-YYYY')
+ (b.created - b.created), 'HH24:MI:SS' ) AS Diff
I need help on how to write the subtraction part since b.created max and min are from the same column.
You can put that query in a subquery and do the subtraction outside:
SELECT *
, max_created - min_created as date_difference
FROM
(
SELECT d.FK_SOCRD_ID
, d.SUBJECT_NUMBER
, c.PTLASTNAME || ', ' || c.PTFIRSTNAME AS "Patient name"
, min(b.created) AS min_created
, max(b.created) AS max_created
FROM CR_MDT a
FULL JOIN CR_MDT_VERIFY b
ON a.CR_MDT_ID = b.FK_CR_MDT_ID
LEFT JOIN patient c
ON c.SOCRD_ID = a.FK_SOCRD_ID
LEFT JOIN PT_STUDY d
ON d.FK_SOCRD_ID = c.SOCRD_ID
WHERE a.CR_MDT_DT BETWEEN TO_DATE('01/01/2017', 'mm/dd/yyyy') AND TO_DATE('12/31/2017', 'mm/dd/yyyy')
GROUP BY d.FK_SOCRD_ID
, d.SUBJECT_NUMBER
, c.PTLASTNAME
, c.PTFIRSTNAME
) subq
You might have to change the datediff function depending on the dbms you are using.
EDIT: I updated the date subtraction part now that i know you use Oracle.
You can use floor(), leave only fractional part of difference and convert it to hh24:mi:ss, like here:
select id, mx, mn, floor(diff) ||' days and '||
to_char(trunc(sysdate) + diff - floor(diff), 'hh24:mi:ss') diff
from (select id, max(created) mx, min(created) mn,
max(created) - min(created) diff
from test group by id)
Test data:
create table test (id number(3), created date);
insert into test values (1, timestamp '2017-09-12 01:00:00');
insert into test values (1, timestamp '2017-11-05 11:24:17');
insert into test values (1, timestamp '2017-12-15 13:42:05');
insert into test values (2, timestamp '2017-01-05 11:00:00');
insert into test values (2, timestamp '2017-06-05 23:12:56');
Result:
ID MX MN DIFF
---- ------------------- ------------------- ---------------------
1 2017-12-15 13:42:05 2017-09-12 01:00:00 94 days and 12:42:05
2 2017-06-05 23:12:56 2017-01-05 11:00:00 151 days and 12:12:56
Here is what gave me the desired result:
SELECT d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME || ', ' || c.PTFIRSTNAME AS "Patient name",
min(b.created) AS min_created,
max(b.created) AS max_created,
trunc(max(b.created)) - trunc(min(b.created)) || ' days' AS Diff
FROM CR_MDT a
FULL JOIN CR_MDT_VERIFY b
ON a.CR_MDT_ID = b.FK_CR_MDT_ID
LEFT JOIN patient c
ON c.SOCRD_ID = a.FK_SOCRD_ID
LEFT JOIN PT_STUDY d
ON d.FK_SOCRD_ID = c.SOCRD_ID
WHERE a.CR_MDT_DT BETWEEN TO_DATE('01/01/2017', 'mm/dd/yyyy') AND TO_DATE('12/31/2017', 'mm/dd/yyyy')
GROUP BY d.FK_SOCRD_ID,
d.SUBJECT_NUMBER,
c.PTLASTNAME,
c.PTFIRSTNAME
I am a novice trying to work through this here with no luck so far, any help is greatly appreciated!!!
Select Distinct
(AB.agency_no || '-' || ab.branch_no) AS "AGENCY-BRANCH",
count (AB.agency_no || '-' || ab.branch_no) AS Occurences,
A.AGY_NAME AS AGENCY,
Sum(AB.annual_premium) as Premium
From Agency_Book_View AB, Agency A, Branch B
Where AB.agency_no = A.Agency_No
AND B.EXPIRATION_DATE = TO_DATE('12-31-2078', 'MM-DD-YYYY')
AND B.EFFECTIVE_DATE <= sysdate and b.effective_date >=sysdate - 364
Group by AB.agency_no || '-' || ab.branch_no, A.Agy_Name, ab.annual_premium
Order by AB.agency_no || '-' || ab.branch_no
So I am trying to return total annual premium per "agency-branch" and I am getting multiple occurrences of agency-branch. I am trying to get one line per agency branch. I hope this is clear. I tried to include a result set but wasnt allowed to include a picture in my post.
Thanks very much!
Brad
Try this :
SELECT (AB.agency_no || '-' || AB.branch_no) AS "AGENCY-BRANCH",
COUNT(AB.agency_no || '-' || AB.branch_no) AS Occurences,
A.AGY_NAME AS AGENCY,
SUM(AB.annual_premium) AS Premium
FROM Agency_Book_View AB, Agency A, Branch B
WHERE AB.agency_no = A.Agency_No AND AB.branch_no = B.branch_no
AND B.EXPIRATION_DATE = TO_DATE('12-31-2078', 'MM-DD-YYYY')
AND B.EFFECTIVE_DATE <= SYSDATE AND B.effective_date >= SYSDATE - 364
GROUP BY AB.agency_no || '-' || AB.branch_no, A.Agy_Name
ORDER BY AB.agency_no || '-' || AB.branch_no
I joined B table and AB table, removed the DISTINCT and the GROUPed BY ab.annual_premium.
I think you need to remove ab.annual_premium from the group by clause.