I trying to write the following SQL code in DAX.
select SUM(amount) from TABLE
where BusinessDate = '2023-02-11'
OR (Segment = 'General' and Days_Without_Payments < 210)
OR (Segment = 'Enterprise' and Days_Without_Payments < 1215)
OR (Segment = 'House' and Days_Without_Payments < 1945)
I got the first part(i-e. SUM) right, but can't get my head around the filters.
What I tried:
Measure = CALCULATE([Total Outstanding],
FILTER(VALUES(BILBL_WO[Date]),BILBL_WO[Date]=MAX(BILBL_WO[Date])),
AND(BILBL_WO[Days_Without_Payments] < 210, BILBL_WO[Segment] = "General") ||
AND(BILBL_WO[Days_Without_Payments] < 1215, BILBL_WO[Segment] = "Enterprise") ||
AND(BILBL_WO[Days_Without_Payments] < 1945, BILBL_WO[Segment] = "House")
)
Where Total Outstanding is another Measure which I created for summation of Amount.
Please help as I couldn't find something useful from internet and I am new at this. Thanks!
In general, when people start learning DAX, most think it is like SQL query, but that is not true because SQL is a structured query language. In contrast, DAX is a formula language used for analysis purposes similar to excel equations.
I will give you a few pointers on how to convert SQL to DAX in simple terms considering you have one table that you imported to Power BI or SSAS tabular model, which are as follow:
You need to segregate your query as follows:
Aggregation function in your example SUM (amount) in SQL query will be in DAX:
Total Outstanding = sum('BILBL_WO'[amount])
Where condition on a date column:
I usually create a date dimension and create a relation with the table that has the date column. You can do that when you go into more details relating to evaluation context in Power BI and Star schema architecture as a start, but in your case, if the column data type is a string, convert it to date type and then use the date column as a filter in the power bi report page, allowing the report users to select different dates, not just ‘2023-02-11'.
For your conditions on Segment and Days_Without_Payments, before converting it to DAX, I think your original query is missing Parenthesis. If I understand it correctly, please let me know if I am wrong.
select SUM(amount)
from TABLE
where BusinessDate = '2023-02-11'
and
(
(Segment = 'General' and Days_Without_Payments < 210)
OR
(Segment = 'Enterprise' and Days_Without_Payments < 1215)
OR
(Segment = 'House' and Days_Without_Payments < 1945)
)
Altered SQL script
If my above assumption is valid and you need to write a DAX measure with the Segment and Days_Without_Payments conditions, you can write it as follow:
Measure = CALCULATE([Total Outstanding] ,
KEEPFILTERS(
FILTER(
ALL(BILBL_WO[Segment] ,BILBL_WO[Days_Without_Payments]),
(BILBL_WO[Segment] = "General" && BILBL_WO[Days_Without_Payments] < 210)
||
(BILBL_WO[Segment] = "Enterprise" && BILBL_WO[Days_Without_Payments] < 1215)
||
(BILBL_WO[Segment] = "House" && BILBL_WO[Days_Without_Payments] < 1945))
)
)
And for more on how to filter DAX using multicolumn, see this YouTube video https://www.youtube.com/watch?v=kQjYG6TJVp8 and follow the SQLBI channel. It will help you a lot.
I hope I helped in a way; if so, please mark this as an answer and vote for it :)
Related
I'm trying recreate a join between 2 tables, timeregistration and schedule, into one table which I then filter based the difference between the scheduled start time and the actual start time. Basically; I'm trying to determine whether a employee is late or early.
My query in SQL looks like this and works as desired, I just can't seem to figure out how to make this join work in DAX for Power BI, let alone the time constraint.
SELECT
tr.employee_employeeID AS EmployeeID,
tr.rawstarttime AS Actual_Start,
s.StartTime_Schedule AS Schedule_Start
FROM
timeregistration tr,
schedule s
WHERE
tr.Employee_EmployeeID = s.Employee_EmployeeID
AND
DATEDIFF(MINUTE, tr.RawStartTime, s.StartDateTime) < 60
AND
DATEDIFF(MINUTE, tr.RawEndTime, s.StartDateTime) > -60
And the tables and relation look something like this:
Table structure
I have tried the following so far:
GENERATE(timeregistration; schedule)
- which returned
The Column with the name of 'EmployeeID' already exists in the 'date_diff' Table.
NATURALINNERJOIN(timeregistration; schedule)
- which returned
No common join columns detected. The join function 'NATURALINNERJOIN' requires at-least one common join column.
CROSSJOIN(timeregistration; schedule)
- which returned
The Column with the name of 'EmployeeID' already exists in the 'date_diff' Table.
As of right now I wouldn't really know what to do with the JOIN, so any help would be appreciated.
With kind regards,
Martien
(edit: Fixed formatting mistakes)
Solved it with this DAX query
date_diff =
FILTER(
ADDCOLUMNS(
GENERATEALL(
schedule;
VAR schedule_id = schedule[EmployeeID]
RETURN
SELECTCOLUMNS(
CALCULATETABLE(
timeregistration;
timeregistration[EmployeeID] = schedule_id
);
"StartTime_timeregistration"; timeregistration[StartTime_actual]
)
);
"diff"; DATEDIFF([StartTime_schedule];[StartTime_timeregistration];SECOND)
);
3600 >= [diff] &&
-3600 <= [diff] &&
NOT(ISBLANK([StartTime_timeregistration]))
)
I have a fact Table that contains people & Thier qualifications. The fact table has two dates, the StartStudyDate & EndStudyDate. This represents the period the students were studying.
I then have a Person Dimension, Qualification Dimension, a Grouping Dimension & one Date Dimension.
Im trying to find a count of students who were actively studying on a particular date.
In SQL its relatively simple:
select a.PaygroupDescription, a.[Qualification], count(a.[PersonID])
from (
select distinct p.[PersonID], PaygroupDescription, q.[Qualification]
from [hr].[Appointment Detail] ad
join hr.Paygroup pg on ad.PaygroupID = pg.PaygroupID
join hr.Qualification q on q.QualificationID = ad.QualificationID
join hr.Person p on p.PersonID = ad.PersonID
join dimdate sd on sd.DateID = ad.StartDateID
join dimDate ed on ed.DateID = ad.EndDateID
where sd.date <= 20150101 and ed.date >= 20150101
) as a
group by a.PaygroupDescription, a.[Qualification]
The problem is i cant figure out how to do this in dax.
I started out by adding two columns to the fact table in the TabularModel:
ActualStartDate
=LOOKUPVALUE(
'Date'[Date],
'Date'[DateID],
'Appointment Detail'[StartDateID])
ActualEndDate
=LOOKUPVALUE(
'Date'[Date],
'Date'[DateID],
'Appointment Detail'[EndDateID])
I then wrote the measure that checks if one date is selected from DimDate, it gets all distinct rows where the selectedDate is <= ActualStartDate && >= ActualEndDate.
Problem is that this behaves like an absolute dog. if i try to add any attributes for breaking the data down, i run out of memory (at least in 32bit excel). I know i could try 64 bit excel, but my dataset is small, so memory should not be an issue. This is before i even add filters to the calculation for specific qualifications:
EmployeeCount:=if(HASONEVALUE('Date'[Date]),
CALCULATE
(distinctcount('Appointment Detail'[PersonID]),
DATESBETWEEN('Date'[Date], min('Appointment Detail'[ActualStartDate]),Max( 'Appointment Detail'[ActualEndDate]))
)
,BLANK())
Id appreciate help in understanding the problem correctly as im obviously missing something here regarding the problem & my dax experience is also very light.
I would remove the relationships to DimDate and then use something like the following pattern for your measure:
EmployeeCount := CALCULATE( COUNTROWS ( 'Person' ),
FILTER('Appointment Detail',
'Appointment Detail'[ActualStartDate] <= MAX(DimDate[Date])
&& 'Appointment Detail'[ActualEndDate] >= MIN(DimDate[Date])
))
I'm trying to avoid using straight up SQL in my Rails app, but need to do a quite large version of this:
SELECT ds.product_id,
( SELECT SUM(units) FROM daily_sales WHERE (date BETWEEN '2015-01-01' AND '2015-01-08') AND service_type = 1 ) as wk1,
( SELECT SUM(units) FROM daily_sales WHERE (date BETWEEN '2015-01-09' AND '2015-01-16') AND service_type = 1 ) as wk2
FROM daily_sales as ds group by ds.product_id
I'm sure it can be done, but i'm struggling to write this as an active record statement. Can anyone help?
If you must do this in a single query, you'll need to write some SQL for the CASE statements. The following is what you need:
ranges = [ # ordered array of all your date-ranges
Date.new(2015, 1, 1)..Date.new(2015, 1, 8),
Date.new(2015, 1, 9)..Date.new(2015, 1, 16)
]
overall_range = (ranges.first.min)..(ranges.last.max)
grouping_sub_str = \
ranges.map.with_index do |range, i|
"WHEN (date BETWEEN '#{range.min}' AND '#{range.max}') THEN 'week#{i}'"
end.join(' ')
grouping_condition = "CASE #{grouping_sub_str} END"
grouping_columns = ['product_id', grouping_condition]
DailySale.where(date: overall_range).group(grouping_columns).sum(:units)
That will produce a hash with array keys and numeric values. A key will be of the form [product_id, 'week1'] and the value will be the corresponding sum of units for that week.
Simplify your SQL to the following and try converting it..
SELECT ds.product_id,
, SUM(CASE WHEN date BETWEEN '2015-01-01' AND '2015-01-08' AND service_type = 1
THEN units
END) WK1
, SUM(CASE WHEN date BETWEEN '2015-01-09' AND '2015-01-16' AND service_type = 1
THEN units
END) WK2
FROM daily_sales as ds
group by ds.product_id
Every rail developer sooner or later hits his/her head against the walls of Active Record query interface just to find the solution in Arel.
Arel gives you the flexibility that you need in creating your query without using loops, etc. I am not going to give runnable code rather some hints how to do it yourself:
We are going to use arel_tables to create our query. For a model called for example Product, getting the Arel table is as easy as products = Product.arel_table
Getting sum of a column is like daily_sales.project(daily_sales[:units].count).where(daily_sales[:date].gt(BEGIN_DATE).where(daily_sales[:date].lt(END_DATE). You can chain as many wheres as you want and it will be translated into SQL ANDs.
Since we need to have multiple sums in our end result you need to make use of Common Table Expressions(CTE). Take a look at docs and this answer for more info on this.
You can use those CTEs from step 3 in combination with group and you are done!
Firstly, I know ORA-000937 is a common issue, with an obvious answer, but I am yet to find any results that could point to a possible solution.
Quick Spec;
National TB/HIV report, based on patient medical records/encounters/visits and drug's provided. This is only a tiny portion of the report, which loops all patient drugs, and calculates most of it's figures off date calculations, we do not store historic/aggregated data, everything is aggregated when requested. I mention this because I expect a few suggestions to move away from GTT's and to rather use MVIEW's - I hear you, but no, not a solution.
Here is my problem, this is one of my queries populating a GTT, within a function, which stores aggregated results. I have structured my data collection in such away as to reduce server load as the medical table exceeds 12 million records. (Each patient has 3 per default).
Here is the GTT
CREATE GLOBAL TEMPORARY TABLE EKAPAII.TEMP_ART_VISIT_MEDS
(
EPISODE_ID NUMBER,
LAST_MEDS_DATE DATE
)
ON COMMIT DELETE ROWS
RESULT_CACHE (MODE DEFAULT)
NOCACHE;
CREATE UNIQUE INDEX EKAPAII.TEMP_ART_VISIT_MEDS_PK ON EKAPAII.TEMP_ART_VISIT_MEDS
(EPISODE_ID);
ALTER TABLE EKAPAII.TEMP_ART_VISIT_MEDS ADD (
CONSTRAINT TEMP_ART_VISIT_MEDS_PK
PRIMARY KEY
(EPISODE_ID)
USING INDEX EKAPAII.TEMP_ART_VISIT_MEDS_PK
ENABLE VALIDATE);
And my simple insert query
INSERT INTO temp_art_visit_meds (EPISODE_ID, LAST_MEDS_DATE)
SELECT episode_id, encounter_date + number_of_days
FROM ( SELECT enc_meds.episode_id,
MAX (enc_meds.encounter_date) encounter_date,
MAX (
CASE
WHEN (NVL (meds.number_of_days, 0) > 150)
THEN
90
ELSE
NVL (meds.number_of_days, 0)
END)
number_of_days
FROM temp_art_visit enc_meds,
vd_medication meds,
dl_drugs_episode_class dlc,
( SELECT latest_meds_visit.episode_id,
MAX (latest_meds_visit.encounter_date)
encounter_date
FROM temp_art_visit latest_meds_visit,
vd_medication latest_meds,
dl_drugs_episode_class dc
WHERE latest_meds_visit.encounter_id =
latest_meds.encounter_id
AND latest_meds.drug_id = dc.drug_id
AND dc.sd_drug_application_id = 8401
GROUP BY latest_meds_visit.episode_id) latest_meds
WHERE enc_meds.encounter_id = meds.encounter_id
AND enc_meds.episode_id = latest_meds.episode_id
AND enc_meds.encounter_date =
latest_meds.encounter_date
AND meds.drug_id = dlc.drug_id
AND dlc.sd_drug_application_id = 8401
AND meds.active_flag = 'Y'
GROUP BY enc_meds.episode_id);
Now my error, is ORA-000937 not a single-group group function, but if I run this query in a normal editor window it works, but I get ORA-000937 when executing the select query in the package body itself, calling the function does not return any error, even though I have an exception block to handle any errors.
Any help will do, I do understand that this errors could occur only at runtime, and not at compile time? Or is it the fact that I am running the query from the pl/sql block?
Toad for Oracle version 12.5 - in all it's glory. (sarcasm)
Again, pardon me if this has already been asked/answered.
EDIT - SOLUTION
So, after a few hours of trouble shooting, I was able to understand why this error is being generated. Firstly, the fixed query;
INSERT INTO temp_art_visit_meds (EPISODE_ID, LAST_MEDS_DATE)
SELECT enc_meds.episode_id ,
TRUNC( MAX (enc_meds.encounter_date)) + MAX (CASE WHEN (NVL (meds.number_of_days, 0) > 150) THEN 90 ELSE NVL (meds.number_of_days, 0) END) last_meds_date
FROM temp_art_visit enc_meds,
vd_medication meds,
dl_drugs_episode_class dlc,
( SELECT latest_meds_visit.episode_id,
MAX (latest_meds_visit.encounter_date) encounter_date
FROM temp_art_visit latest_meds_visit,
vd_medication latest_meds,dl_drugs_episode_class dc
WHERE latest_meds_visit.encounter_id = latest_meds.encounter_id
AND latest_meds.drug_id = dc.drug_id
AND dc.sd_drug_application_id = 8401
GROUP BY latest_meds_visit.episode_id) latest_meds
WHERE enc_meds.encounter_id = meds.encounter_id
AND enc_meds.episode_id = latest_meds.episode_id
AND enc_meds.encounter_date = latest_meds.encounter_date
AND meds.drug_id = dlc.drug_id
AND dlc.sd_drug_application_id = 8401
AND meds.active_flag = 'Y'
GROUP BY enc_meds.episode_id, meds.number_of_days, enc_meds.encounter_date;
It would appear that problem was due to the amount of sub-queries, I attempted to use different hint optimizers with no avail. If you look closely at the first query, you will notice I am basically aggregating results from aggregated results, so the column's encounter_date, episode_id, number_of_days are no longer 'available', so even if I added the appropriate GROUP BY clause to my last (outer) subquery, Oracle would not be able to group on those column names/identifiers.
I am not sure why this would fail only in a PACKAGE BODY, and did not return any SQLERR or SQLCODE when executed.
Happy days.
I'm just learning Qlikview and am struggling with the syntax for set analysis. I have a dimension called OPEN_DT which is a date field. For this little exercise I just want to have the expression count the records where the date is on or before 02/27/2014. Step 2 will be to make the date dynamic but for now I'm at the "Hello, World" stage.
I've tried the following and what feels like 50 variations thereof...
COUNT({
<
OPEN_DT = {"<{(= Date('2014-02-27', 'yyyy-MM-dd'))} "}
>} MTTER_ID)
Can anyone help me sort this out?
This should work:
COUNT({
<
OPEN_DT = {"$(=Date(Date#('2014-02-27', 'YYYY-MM-DD')))"}
>} MTTER_ID)
or with a variable (using calendar object):
COUNT({
<
OPEN_DT = {"$(=Date(vTheDate))"}
>} MTTER_ID)