I am not a programmer by trade but I know some SQL. I just need another set of eyes on my code because I am not sure why I am getting this error.
select
count(1) AH
from regmdr.contact_interaction ci
join regmdr.source_data sd on ci.sd_id = sd.sd_id
join regmdr.account_contact ac on ci.acct_cont_id = ac.acct_cont_id
join regmdr.account acc on ac.acc_id = acc.acc_id
where sd.sd_user_type in ('1','2')
and sd.sd_origin_reference = 'www.alliancehealth.com'
and ci.ci_create_date in (select case when ci.ci_create_date between to_date('1/1/2016','mm/dd/yyyy') and to_date('1/31/2016','mm/dd/yyyy') then 'January'
when ci.ci_create_date between to_date('2/1/2016','mm/dd/yyyy') and to_date('2/29/2016','mm/dd/yyyy') then 'February'
when ci.ci_create_date between to_date('3/1/2016','mm/dd/yyyy') and to_date('3/31/2016','mm/dd/yyyy') then 'March'
else '' end from regmdr.contact_interaction ci group by to_char(ci.ci_create_date, 'yyyy-mm')
You are trying to compare the ci.ci_create_date date value with the string that is produced by your case statement, which is January, February, March or null. So you're effectively doing a comparison like:
ci.ci_create_date = to_date('January')
Unless your NLS_DATE_FORMAT is 'Month' and your language is English, that will get the ORA-01858. You could convert the left-hand side of that to the month name too, but without the year in either format model that would include data from January in any year; and it's better for performance to not convert the data from the table if you can avoid it.
It isn't entirely clear what you're trying to do but as the subquery has an independent view of contact_interaction with no correlation it probably isn't going to do whatever you are trying anyway.
If you're trying to count values from the first three months of this year then you can do:
select count(1) AH
from regmdr.contact_interaction ci
join regmdr.source_data sd on ci.sd_id = sd.sd_id
join regmdr.account_contact ac on ci.acct_cont_id = ac.acct_cont_id
join regmdr.account acc on ac.acc_id = acc.acc_id
where sd.sd_user_type in ('1','2')
and sd.sd_origin_reference = 'www.alliancehealth.com'
and ci.ci_create_date >= date '2016-01-01'
and ci.ci_create_date < date '2016-04-01'
Which will give you a single number. If you want it by month your group by was in the wrong place, and you can add
...
group by trunc(ci.ci_create_date, 'MM')
although you really need that in the select list too for the result set to make any sense - so you know which month each count belongs to.
Based on using the month names at all, perhaps you wanted those in the select list:
select to_char(trunc(ci.ci_created_date, 'MM'), 'Month') as month,
count(1) as AH
from regmdr.contact_interaction ci
...
group by trunc(ci.ci_create_date, 'MM')
... but I'm speculating even more now. Also be aware that month names are sensitive to your NLS settings, particularly NLS_DATE_LANGUAGE. You can force them to always be in English via the optional third argument to to_char() though, e.g.
select to_char(trunc(ci.ci_created_date, 'MM'), 'Month', 'NLS_DATE_LANGUAGE=ENGLISH')
...
Why not go with:
select to_char(ci.ci_create_date, 'YYYY-MM') monthyear,
count(1) AH
from regmdr.contact_interaction ci
join regmdr.source_data sd on ci.sd_id = sd.sd_id
join regmdr.account_contact ac on ci.acct_cont_id = ac.acct_cont_id
join regmdr.account acc on ac.acc_id = acc.acc_id
where sd.sd_user_type in ('1','2')
and sd.sd_origin_reference = 'www.alliancehealth.com'
and to_char(ci.ci_create_date, 'MON' in ('JAN', 'FEB', 'MAR')
group by to_char(ci.ci_create_date, 'yyyy-mm';
If you are only interested in the counts (without context), wrap it in an outside select statement:
select AH
from (select to_char(ci.ci_create_date, 'YYYY-MM') monthyear,
count(1) AH
from regmdr.contact_interaction ci
join regmdr.source_data sd on ci.sd_id = sd.sd_id
join regmdr.account_contact ac on ci.acct_cont_id = ac.acct_cont_id
join regmdr.account acc on ac.acc_id = acc.acc_id
where sd.sd_user_type in ('1','2')
and sd.sd_origin_reference = 'www.alliancehealth.com'
and to_char(ci.ci_create_date, 'MON') in ('JAN', 'FEB', 'MAR')
group by to_char(ci.ci_create_date, 'yyyy-mm'
);
Related
I have this query below and I need to change WHERE conditions depending on QUARTER.
Meaning that I have to copy that query and change the DATE conditions to 06.2020 then use UNION.
I have no idea how can I optimize this query only with SQL because I am not able to DEFINE some variables/parameters in SQL (not using PL/SQL).
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('03.2020', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('03.2020', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('03.2020', 'MM.YYYY'))
Thanks for help
If SQL*Developer, or SQLPlus, you can use the 'substitution variables'.
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&1', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&1', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&1', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&1', 'MM.YYYY'))
When you run this it will prompt you for the value.
If you used just '&1' it would prompt you for each occurence.
'&&1' reuses the value of the first occurence.
You could specify different variables using &1, &2, ...
Also you may used named variable as follows:
To prompt for the date :
ACCEPT dt CHAR PROMPT 'ENter the date (MM.YYYY): '
Or setting the value at declaration:
DEFINE dt='03.2020'
Then:
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&dt', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&dt', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&dt', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&dt', 'MM.YYYY'))
Ok first of all a disclaimer:
As I know nothing about your original query's logic - the query I propose only shows the main logic - and probably will not run as is.
Having said that - the approach would be:
Based on parameters :start_y (start year) and :end_y (end year)
generate a list with all the quarters for those years years_and_quarters.
Join the years_and_quarters with the required table based on quarter start and end dates - and then calculate the sums grouping by year and quarter.
with quarters as
(
select 'Q1' quarter, '03' q_end_date, '01' q_start_date from dual
union
select 'Q2' quarter, '06' q_end_date,'04' q_start_date from dual
union
select 'Q3' quarter, '09' q_end_date,'07' q_start_date from dual
union
select 'Q4' quarter, '12' q_end_date,'10' q_start_date from dual
),
years as
(select :start_y + (level-1) as mydate
from dual
connect by level <= (:end_y - :start_y) +1
),
years_and_quarters as (
select * from years,quarters
order by mydate
),
accounts as (
SELECT a.limit_amount ,
b.balance_amount,
'LOAN' as TYPE, b.balance_date,a.account_open_date,a.account_close_date
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
)
select sum(accounts.limit_amount) as LIMIT,
sum(accounts.balance_amount) as OUTSTANDING,
years_and_quarters.quarter,
accounts.TYPE
from
years_and_quarters,accounts
where
trunc(balance_date) = last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
and trunc(account_close_date) > last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY')) and trunc(account_open_date) <= last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
group by years_and_quarters.mydate,years_and_quarters.quarter
How about just aggregate by the quarter?
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
to_char(balance_date, '"Q"Q YYYY') as quarter
FROM accounts a LEFT JOIN
account_balances b
ON a.account_key = b.account_key AND
b.balance_type_key = 16 AND
b.balance_date = LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
WHERE a.account_close_date > LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH) AND
a.account_open_date <= LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
GROUP BY TRUNC(balance_date, 'Q') ;
Your query seems focused on the last day of the quarter. This arrives at that by adding two months to the start and using LAST_DAY().
I want to add add the Year to date component to this code. I have tried some other ways but I am not getting what I would like to see. Can someone please help me revised this to include the YTD in addition to the Month to date that is already there?
SELECT
COST__DESC,
ST.AD_SRV_MTN AS MONTH_OF_AD,
COUNT(DISTINCT CM.CM_NBR) AS CMS,
MEM_MO AS MBR_MTH,
CMS/MBR_MTH*1000 AS CMS_PER_1000
FROM XTR.FT_CM AS CM
JOIN XTR.FT_ST AS ST ON ST.CM_NBR = CM.CM_NBR
JOIN XTR.DIM_MED_CST AS MC ON ST.CST_CK = MCC.CST_CK
JOIN XTR.DIM_AF AS AFF ON ST.PRO_CK = AFF.AFF_CK
JOIN XTR.DIM_ADJDCTN_STAT AS A_S ON ST.ADJDCTN_STAT_CK = A_S.ADJDCTN_STAT_CK
JOIN XTR.DIM_ADJ_OT AS OT ON ST.ADJ_CK = OT.ADJ_CK
LEFT JOIN
(SELECT
CALENDAR_YEAR_MONTH as YEAR_MO,
SUM(MBR.COUNT_NBR) as MEM_MO
FROM XTR.FT_MBR_MONTHS MBR
INNER JOIN DIM_MBR_C ON MBR.DB_MBR_CK = DIM_MBR_C.DB_MBR_CK
AND MBR.DATE_CK BETWEEN DIM_MBR_C.DB_eff_date_ck
AND DIM_MBR_C.DB_END_DATE_CK
INNER JOIN DIM_DATE DT ON ELI_DATE_CK = DT.DATE_CK
WHERE MBR.F_C_CK = 500058321 AND YEAR_MO >= 201701
GROUP BY 1) MM ON ST.AD_SRV_MTN = MM.YEAR_MO
WHERE ST.F_C_CK = 500058321 AND ST.ST_START_DATE_CK >= 20200101
AND ST.AD_SRV_MTN > 201912 AND MC.MED_DESC IN ('Er', 'IP')
AND ST.AD_SRV_MTN < ((EXTRACT (YEAR FROM CURRENT_DATE) *100) +
EXTRACT (MONTH FROM CURRENT_DATE))
GROUP BY 1,2,4
ORDER BY 1,2
Honestly I don't really get your SQL and what is counted, but: Your can play with dates quite easy in Teradata, as Dates are stored (and can be used) internally as INTEGER. Just keep in mind year 1900 as year 0 and format YYYYMMDD.
So e.g. 16-Apr-2020 is in Format YYYYMMDD 20200416 and if you take 1900 as 0 you'll end up with 1200416 which is the internal format. Just try SELECT CURRENT_DATE (INT); - So if you want compare YearNumers you just have to divide by 10000.
With this your can implement YTD as SUM (CASE WHEN CURRENT_DATE/10000 = <YourDateField>/10000 THEN <YourKPI> else 0 END) as YourKPI_YTD. Counting can be done by SUM...THEN 1 ELSE 0 END....
I work for a CPG company and need to create a report that compares the previous month's delivered units to the next month's forecast. (Simply, our forecasting tool screws up occasionally and this will help identify when the forecast is off.)
My issue is my SQL query is summing forecast sales correctly, but the sum of total delivered is not respecting the dates I have in my WHERE clause -- it's summing total delivered for as far back as the query can reach.
Here is my query:
SELECT
DelUnits.Customer, DelUnits.ObsText01,
FinalFcst.SKU, FinalFcst.Customer,
SUM(DelUnits.Value) AS TotalDelivered,
SUM(FinalFcst.FinalFcst) AS ForecastSales
FROM
DelUnits
LEFT JOIN
FinalFcst ON DelUnits.Customer = FinalFcst.Customer
WHERE
(FinalFcst.DT >= '2018-01-01' and FinalFcst.DT <= '2018-01-31')
AND (DelUnits.Date >= '2017-12-01' and DelUnits.Date <= '2017-12-31')
AND DelUnits.ObsText01 = '10_LB'
AND FinalFcst.SKU = '10_LB'
GROUP BY
DelUnits.Customer, DelUnits.ObsText01, FinalFcst.SKU, FinalFcst.Customer
Again, the query seems to work correctly for the final forecast (summing the forecast between 1/1/18 - 1/31/18) but sums the entire delivery history for a customer. I don't understand why it won't sum the delivery history for just 12/1/17 - 12/31/17.
Thank you for your help!
Presumably, there is only one row for FinalFcst. So, either include it in the GROUP BY clause or use MAX() instead of SUM():
max(FinalFcst.FinalFcst) as ForecastSales
One way to achieve this is to calculate TotalDelivered and ForecastSales in 2 different queries and then join them together.
Try this:
SELECT DelUnits.customer,
DelUnits.obstext01,
FinalFcst.sku,
FinalFcst.customer,
totaldelivered,
forecastsales
FROM (SELECT customer,
obstext01,
Sum(value) AS TotalDelivered
FROM delunits
WHERE date >= '2017-12-01'
AND date <= '2017-12-31'
AND obstext01 = '10_LB'
GROUP BY customer,
obstext01) DelUnits
LEFT JOIN (SELECT customer,
sku,
Sum(finalfcst) AS ForecastSales
FROM finalfcst
WHERE dt >= '2018-01-01'
AND dt <= '2018-01-31'
AND sku = '10_LB'
GROUP BY customer,
sku) FinalFcst ON DelUnits.customer = FinalFcst.customer
You have a many to many relationship between the tables. Ultimately you need to SUM() one table before joining to the other to create a one to many relationship, or you end up duplicating records.
My favorite approach is a derived table:
SELECT C.Customer,
C.ObsText01,
FC.SKU,
C.TotalDelivered,
SUM(FC.FinalFcst) ForecastSales
FROM (SELECT SUM(Value) TotalDelivered, Customer, ObsText01
FROM DelUnits
WHERE Date >= '2017-12-01' AND Date <= '2017-12-31'
AND ObsText01 = '10_LB'
GROUP BY Customer) C
LEFT JOIN FinalFcst FC ON C.Customer = FC.Customer
AND FC.DT >= '2018-01-01'
AND FC.DT <= '2018-01-31'
AND FC.SKU = '10_LB'
GROUP BY C.Customer, C.ObsText01, FC.SKU, C.TotalDelivered
A couple things: Added your forecast table filters to the join predicate, since having those in the WHERE will create an INNER JOIN out of your LEFT JOIN. Also removed FC.Customer from the select and the group since it is redundant with C.Customer.
Maybe you could try to create a temp table to calculate the delivery history. I am not sure of the SQL Server verbiage, but something like this:
WITH DEL_HIST AS
(SELECT DelUnits.Customer,
DelUnits.ObsText01,
sum(DelUnits.Value) as TotalDelivered,
FROM DelUnits
Where(DelUnits.Date >= '2017-12-01' and DelUnits.Date <= '2017-12-31')
and DelUnits.ObsText01 = '10_LB'
Group By DelUnits.Customer, DelUnits.ObsText01)
SELECT
DEL_HIST.Customer,
DEL_HIST.ObsText01,
FinalFcst.SKU,
FinalFcst.Customer,
DEL_HIST.TotalDelivered,
sum(FinalFcst.FinalFcst) as ForecastSales
FROM DEL_HIST
left join FinalFcst ON DelUnits.Customer = FinalFcst.Customer
Where (FinalFcst.DT >= '2018-01-01' and FinalFcst.DT <= '2018-01-31')
and FinalFcst.SKU = '10_LB'
Group By DelUnits.Customer, DelUnits.ObsText01, FinalFcst.SKU, FinalFcst.Customer
The SQL below give me the columns for Account, Name and the Total Sale for the year of 2015.But how, if possible, can I add another column for the previous year of 2014 ?
select a.AcctNo, b.Name, Sum(a.TotSold) as [ Total Sold ]
from Orders as A
Join Accounts as b on a.AcctNo = b.AcctNo
where (a.PurchaseDate between '1/1/2015' and '12/31/2015' )
Group by a.AcctNo, b.Name
You can use conditional aggregation:
select o.AcctNo, a.Name,
Sum(case when year(PurchaseDate) = 2015 then TotSold else 0 end) as tot_2015,
Sum(case when year(PurchaseDate) = 2014 then TotSold else 0 end) as tot_2014
from Orders o Join
Accounts a
on a.AcctNo = o.AcctNo
where PurchaseDate >= '2014-01-01'
PurchaseDate < '2016-01-01'
Group by o.AcctNo, a.Name ;
Notes:
Use ISO standard formats for date constants.
When using dates, try not to use between. It doesn't work as expected when there is a time component. The above inequalities work regardless of a time component.
Use table aliases that are abbreviations of the table name. That makes the query easier to follow.
A quick background -- I need to find the termination rates of cases that go through our company lab grouped by case type and month. So far I came up with this:
SELECT BPI.TYPE,
EXTRACT(MONTH FROM CS.RECEIVED_DATE) MONTH,
COUNT(*) termed_cases
FROM CELL_SOURCE cs
JOIN BASIC_PATHOLOGY_INFO bpi ON CS.CELL_SOURCE_ID = BPI.CELL_SOURCE_ID
JOIN RECENT_CELL_SOURCE_STATUS rcss ON CS.CELL_SOURCE_ID = RCSS.CELL_SOURCE_ID
WHERE type IS NOT NULL
AND CS.RECEIVED_DATE > to_date('03/01/2011', 'MM/DD/YYYY/')
AND RCSS.STATUS like 'Term%'
GROUP BY BPI.TYPE, EXTRACT(MONTH FROM CS.RECEIVED_DATE)
ORDER BY month, type
This finds all cases that have been termed, easy enough. However, when I want to find the rate, I get a bit of a problem. I tried using a sub-query to catch the total amount of cases per type regardless of it's status, as such:
COUNT(*)/(SELECT COUNT(*)
FROM CELL_SOURCE cs_1
JOIN BASIC_PATHOLOGY_INFO bpi_1 ON CS_1.CELL_SOURCE_ID = BPI_1.CELL_SOURCE_ID
WHERE BPI_1.TYPE = BPI.TYPE
AND EXTRACT(month from CS_1.RECEIVED_DATE) = EXTRACT(MONTH FROM CS.RECEIVED_DATE)) termed_cases
However, this throws an ORA-00979: not a GROUP BY expression error, and highlights BPI.TYPE from the sub-query.
Anyone have any idea what my error could actually be? Also, would an analytical function work better here than an aggregate function?
So, you need two counts: a total for all cases in the month and a total for just the Termed cases. The easiest way of doing this is to use a CASE() function to execute a conditional count, like this:
SELECT BPI.TYPE,
EXTRACT(MONTH FROM CS.RECEIVED_DATE) MONTH,
COUNT(*) all_cases,
sum(case when RCSS.STATUS like 'Term%' then 1 else 0 end ) termed_cases
FROM CELL_SOURCE cs
JOIN BASIC_PATHOLOGY_INFO bpi ON CS.CELL_SOURCE_ID = BPI.CELL_SOURCE_ID
JOIN RECENT_CELL_SOURCE_STATUS rcss ON CS.CELL_SOURCE_ID = RCSS.CELL_SOURCE_ID
WHERE tumor_type IS NOT NULL
AND CS.RECEIVED_DATE > to_date('03/01/2011', 'MM/DD/YYYY/')
GROUP BY BPI.TUMOR_TYPE, EXTRACT(MONTH FROM CS.RECEIVED_DATE)
ORDER BY month, tumor_type
Note that I have removed the LIKE filter from the WHERE clause.
select bpi.type , month,
termed_cases /
(select count(*)
from CELL_SOURCE cs_1
inner join BASIC_PATHOLOGY_INFO bpi_1
on CS_1.CELL_SOURCE_ID = BPI_1.CELL_SOURCE_ID
where BPI_1.TUMOR_TYPE = BPI.TUMOR_TYPE
and extract(month from CS_1.RECEIVED_DATE) = extract(MONTH FROM CS.RECEIVED_DATE)
)
from (
select BPI.TYPE,
extract(MONTH FROM CS.RECEIVED_DATE) MONTH,
count(*) termed_cases
from CELL_SOURCE cs
inner join BASIC_PATHOLOGY_INFO bpi
on CS.CELL_SOURCE_ID = BPI.CELL_SOURCE_ID
inner join RECENT_CELL_SOURCE_STATUS rcss
on CS.CELL_SOURCE_ID = RCSS.CELL_SOURCE_ID
where tumor_type is not null
and CS.RECEIVED_DATE > to_date('03/01/2011', 'MM/DD/YYYY/')
and RCSS.STATUS like 'Term%'
group by BPI.TYPE, extract(MONTH FROM CS.RECEIVED_DATE)
)
order by month, type