Laravel Eloquent Sum Column Between Two Dates for Given Interval - laravel

I have a bookings table that has a check_in and check_out columns in addition to deposit and balance columns. The check_in and check_out are date columns while deposit and balance is decimal column.
I want to generate a report for revenue that sums the deposit and balance columns for a given date interval (supplied from the UI). Here is what I have:
$deposit = Booking::whereRaw("check_in >= '$start'")
->whereRaw("check_out <= '$end'")
->sum('deposit');
$balance = Booking::whereRaw("check_in >= '$start'")
->whereRaw("check_out <= '$end'")
->sum('balance');
$revenue = $deposit + $balance;
I am however getting wrong figures. What am I doing wrong?

When you chain whereRaw you get the result for the records that have both, as AND. Maybe you want OR
Let's say we have this table
DB::table('checkout')->whereRaw("check_in >= 5")->whereRaw("check_out < 5")->sum("sum") gets you the result 0, since none of the records have check_out < 5 and check_in >= 5 at the same time.
DB::table('checkout')->whereRaw("check_in >= 5 OR check_out < 5")->sum("sum") gets you 10.
This should point you in the right way.

Related

Calculate the sum grouped by 2 other columns

Please I really need your help on this, Hello,
I need to create a measure using DAX that has the sum of the qty, I should take the max date of the table that is between two other dates (Date Slicer) based on the column name, For example, if the selected date of the slicer is between 24/6/2020 and 20/3/2021, I need to calculate the total (sum of the qty of each name 'column' where it's date is the max date that is less then the max selected date
So as you can see in the figure the total should be 50+129+3(For that selected date)
Quantity =
VAR _maxdate =
MAX ( 'Date Table'[Date] )
RETURN
SUM ( CALCULATE([Qty], 'Fact Table'[Date] <= _maxdate )
The CALCULATE function in DAX is very powerful. Essentially, you can pass filter criteria into it to create aggregations. In this case, it sounds like you want to sum [Qty] when the date is < the max date.
Since your table visuals aren't automatically updating, you also may not be implementing a date table correctly.

Add arbitrary column based on some calculation

I need to add some custom values to a report and I am trying to add a column based on some logic. Is it possible to add a "cost" column based on the age range calculated from a date of birth column?
'Cost' AS (CASE WHEN TO_CHAR(SYSDATE,'yyyy')-TO_CHAR(DOB.BIRTHDATE,'yyyy') >= 0 AND TO_CHAR(SYSDATE,'yyyy')-TO_CHAR(DOB.BIRTHDATE,'yyyy') < 25) THEN 'Cost' = 0.05) END AS "Cost"
That would be
select
CASE WHEN to_number(TO_CHAR(SYSDATE,'yyyy')) - to_number(TO_CHAR(DOB.BIRTHDATE,'yyyy')) >= 0 AND
to_number(TO_CHAR(SYSDATE,'yyyy')) - to_number(TO_CHAR(DOB.BIRTHDATE,'yyyy')) < 25
THEN 0.05
END AS cost
from some table
You used wrong syntax, double quotes (where you shouldn't - that's not an error, but it's not needed either), subtracted characters.

Using SUMMARIZE to Count Values for a Range of Dates

I am trying to use SUMMARIZE to generate a table containing a list of people (represented by Phishing[Zid]) and the number of times where the person had a "Phishing Outcome" of "Fail" for a 12 month period. The 12 month period for each person will start on the date represented by Calendar[Month Index] and then I want to count the failures between that month index and month index - 11.
For example, assume the current row in the Phishing table has a Zid of wzqc51 and Calendar[Month Index] = 27. In the CALCULATE function I want to count the number of fails for month index between 16 and 27. It's the piece in angle brackets below that I cannot get right.
Also, there is a 1:M between Calendar and Phishing and the Calendar table is not a traditional daily calendar. It is a monthly calendar where month index of 1 is the first year and month of data and month index of say 27 is the 27th year and month of data we have.
SUMMARIZE(
Phishing,
Phishing[Zid],
Calendar[Month Index],
"Count", CALCULATE(
COUNT(Phishing[Zid]),
< I want to calculate a count between the current month index and month index - 11 >
Phishing2[Phishing Outcome]= "Fail"
)
)
The output should look like this:
Zid Month Index Count
QZ4TIN 27 4
This would mean that the user with a Zid of QZ4TIN failed four times between month index 16 and 27.
It's a bit tricky to write DAX without having data to test against, but I suspect you want something like
FILTER (
ALL ( Calendar ),
Calendar[Month Index] <= EARLIER ( Calendar[Month Index] ) &&
Calendar[Month Index] >= EARLIER ( Calendar[Month Index] ) - 11
)
Where EARLIER refers to the earlier row context (from the SUMMARIZE rather than the FILTER), not anything to do with dates or time.
You may wish to create a variable before the CALCULATE rather than using EARLIER
SUMMARIZE (
Phishing,
Phishing[Zid],
Calendar[Month Index],
"Count",
VAR CurrMonth = Calendar[Month Index]
RETURN
CALCULATE (
COUNT ( Phishing[Zid] ),
FILTER (
ALL ( Calendar ),
Calendar[Month Index] <= CurrMonth &&
Calendar[Month Index] >= CurrMonth - 11
),
Phishing2[Phishing Outcome] = "Fail"
)
)

INDEX ON Date Range and a Uniqe Number VFP

I need to change a select statement in VFP to do a simple task (select all records in a date range and by a Employee Number). I have tried everything I can think of. I know I can do it in SQL select but I just want to use the table I have and not a cursor.
I'm trying something like this
INDEX ON Date >= ThisForm.DateFrom+Date + Date=< ThisForm.DateTo+ALLTRIM(empid) TAG MyOrder
I know how INDEX ON works but my format is wrong.
Maybe you meant a filtered index. However you don't need to do such a thing. You could simply have an index combining employee number (I suppose it is an integer) and the date. Then you can use a simple for and while scoping clauses or range or alike (you really didn't explain what you would do) - and even an SQL might be the easier way depending on what you would do.
ie: (using date as a column name is a bad idea, but that is another matter)
INDEX ON padl(empId, 10, '1') + dtoc(Date,1) TAG MyOrder
Having such an index you could scan all the records for a given employee in a date range like this:
local lnEmployee, lcStart, lcEnd
lnEmployee = 1 && whatever the employee number is
lcStart = padl(m.lnEmployee, 10, '1')+dtoc(ThisForm.DateFrom,1)
lcEnd = padl(m.lnEmployee, 10, '1')+dtoc(ThisForm.DateTo,1)
scan for padl(empId, 10, '1') + dtoc(Date,1) >= m.lcStart and ;
padl(empId, 10, '1') + dtoc(Date,1) <= m.lcEnd
* whatever
endscan
This would do the same:
local lnEmployee, lcStart, lcEnd
lnEmployee = 1 && whatever the employee number is
lcStart = padl(m.lnEmployee, 10, '1')+dtoc(ThisForm.DateFrom,1)
lcEnd = padl(m.lnEmployee, 10, '1')+dtoc(ThisForm.DateTo,1)
set order to tag myOrder
set range to m.lcStart, m.lcEnd
scan
* whatever
endscan
PS: Actually with indexes on empid and date, a simple scan for ... would do too. ie:
scan for empId = m.lnEmpoyee and ;
Date >= ThisForm.DateFrom and ;
Date <= ThisForm.DateTo
*...
endscan
Welp don't really like filter but if any one has a better idea let me know!
Set Filter To Date >= ThisForm.DateFrom .And. Date =< ThisForm.DateTo .And. EmpID = AllTrim(ThisForm.Combo1.Value)

Can can someone explain me the query in the function

CREATE OR REPLACE FUNCTION exlude_weekends (p_date_start DATE,
p_date_end DATE)
RETURN NUMBER
AS
l_no_of_days NUMBER := NULL;
BEGIN
SELECT COUNT ( * ) INTO l_no_of_days
FROM (SELECT date_extraction, TO_CHAR (date_extraction, 'DAY')
FROM (SELECT TO_DATE(p_date_start,'DD-MON-RRRR')
+ LEVEL - 1 date_extraction FROM DUAL CONNECT BY LEVEL <
(TO_DATE (p_date_end, 'DD-MON-RRRR')- TO_DATE (p_date_start,'DD-MON-RRRR'))+ 2)
WHERE TRIM (TO_CHAR (date_extraction, 'DAY')) NOT IN ('SATURDAY', 'SUNDAY'));
RETURN l_no_of_days;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END exlude_weekends;
As you mention in the comments, the key of the function is the hierarchical sub-query:
SELECT TO_DATE(p_date_start,'DD-MON-RRRR') + LEVEL - 1 date_extraction
FROM DUAL
CONNECT BY LEVEL <
(TO_DATE(p_date_end,'DD-MON-RRRR')-
TO_DATE(p_date_start,'DD-MON-RRRR'))+ 2
Hierarchical queries try to traverse a tree (CONNECT BY clause specifies how parents and children are related). In this example we find a tricky use (or abuse) of the connect by.
This sub-query generates date from p_date_start to p_date_end (both inclusive). How it does it?
Note that the expression being compare with LEVEL in the CONNECT BY is a constant, and it is the number of days between start and the day after the end date (why the day after the end date? because it is using < and the day after the end date is the first day out of the interval):
(TO_DATE(p_date_end,'DD-MON-RRRR')-TO_DATE(p_date_start,'DD-MON-RRRR'))+2
The select get the DUAL row (it has only one row) this row has LEVEL 1 (hierarchical query use the pseudo-column LEVEL to indicate the depth from the root where it started to evaluate).
The CONNECT BY checks that this level (1) is in the range of days to be generated.
Evaluates the expression:
TO_DATE(p_date_start,'DD-MON-RRRR') + LEVEL - 1
This is the start date plus the level minus one: this is, the start date.
Now a new cycle in hierarchical evaluation starts: the row generated in the previous cycle (the start date) is evaluated again (the new row will have level 2).
If it is in the range of days to be generated (controlled by the CONNECT BY clause) a new date is generated (the day after the start date).
A new cycle start (level 3)....
And the process iterates until LEVEL is greater than the number of days to be generated (which is the same than the number of levels required to iterate from the start date to the end date).
The outer queries in the function only filter SATURDAYS and SUNDAYS and count the remaining days.
Although oracle is very efficient evaluating this query, this function uses a brute force solution.
A more elegant and mathematical solution can be used (with no iterations). We have an equation that computes the number of a particular day of week between two dates:
TRUNC(( END – START – DAYOFWEEK(END-DAYOFWEEKTOBECOUNTED) + 8) / 7)
where DAYOFWEEK is a function that returns 0-6 (0 Sunday, 1 Monday ... 6 Saturday). And DAYOFWEEKTOBECOUNTED is the number of the day to be counted in the same format.
Note that TO_CHAR(date, 'd') returns the day of week in 1..7 format we must rectify to 0..6 format (In my region monday is the first day of week, so i get sunday as 0 and saturday as 6 with the mod function as follows):
MOD(TO_NUMBER(TO_CHAR(p_date_end, 'd')), 7)
Finally we want the number of days in the interval minus the number of sundays (day 0) and saturdays (day 6). So the final procedure with the mathematical approach will be:
CREATE OR REPLACE FUNCTION exlude_weekends (p_date_start DATE,
p_date_end DATE)
RETURN NUMBER
AS
l_no_of_days NUMBER := NULL;
BEGIN
SELECT TRUNC(p_date_end - p_date_start) + 1 -
( TRUNC((p_date_end - p_date_start -
MOD(to_number(to_char(p_date_end - 0, 'd')), 7)+8)/7)
+ TRUNC((p_date_end - p_date_start -
MOD(to_number(to_char(p_date_end - 6, 'd')), 7)+8)/7)
)
INTO l_no_of_days
FROM DUAL;
RETURN l_no_of_days;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END exlude_weekends;

Resources