I am attempting to determine "First Time Givers", people who gave in FY2015 but never given before. I also need to ignore users who gave for a certain reason (appealCode).
Below is example of some of the fields my tables have and what information needs limited.
**FundLedger**
Account ID EntryAmount GiftReceivedDt AppealCode
1000 $500 7/1/2014 1
1000 $500 2/2/2002 2
2000 $25 8/1/2014 1
2000 $25 9/1/2014 1
3000 $100 10/1/2014 1
4000 $1,000 11/1/2014 2
**ConstituentTotals**
ConstituentID FirstTransactionDate LastTransactionDate CashAmount
1000 2/2/2002 1/1/2014 $1,000
2000 3/1/2014 4/1/2014 $50
3000 5/1/2014 5/1/2014 $100
4000 11/1/2014 11/1/2014 $1,000
What I need is to find the number of constituents who gave between 6/1/2014 and today, who have never given before, and the gift was not given to AppealCode 2.
So the number I need from the sample information is '2'.
**Information Needed**
ConstituentID CashAmount FirstTransactionDate LastTransactionDate AppealCode
2000 $50 3/1/2014 4/1/2014 1
3000 $100 5/1/2014 5/1/2014 1
As of now I can either get the number of people who gave if I ignore the AppealCode, or I can get the AppealCode limited but I get ALL transactions of the giver.
Currently at this stage, it pulls the count 77,000 times, one for each entry in the Ledger.
'SELECT
Number_Of_New_Donors = ( SELECT COUNT(a.ConstituentID)
From dbo.FundConstituentTotals a
RIGHT JOIN dbo.FundLedger b
ON a.ConstituentID = b.AccountID
WHERE (a.FirstTransactionDT between '6/1/2014' and '5/31/2015'
AND a.CashAmount > '0'
AND a.GivingYear = '2015'
AND A.GivingYear !< '2015')
AND (b.GiftReceivedDt between '6/1/2014' and '5/31/2015'
AND b.RecordTypeID != '0'
AND b.RecordTypeID != '-1'
AND b.RecordTypeID != '2'))
FROM FundConstituentTotals'
Suggested Response Results:
ConstituentID FundConstituentTotalID ConstituentID GivingYear PledgeAmount CashAmount NonCashAmount FirstTrans
49427 77314 49427 2015 0 25 0 1/13/2015
49427 77314 49427 2015 0 25 0 1/13/2015
49427 77314 49427 2015 0 25 0 1/13/2015
49427 77314 49427 2015 0 25 0 1/13/2015
Just found that the data is innacurate, FirstTransactionDate does not provide the date of the first transaction, just the date the transaction begin being posted to the ledger (SOMEONE MESSED THIS UP IN THE PAST). I will have to use GiftReceivedDate between DATES, and find a way to remove people if they have dates prior to 2014.
SELECT Number_Of_New_Donors = ( SELECT COUNT(a.ConstituentID)
From dbo.FundConstituentTotals a
LEFT JOIN dbo.FundLedger b
ON a.ConstituentID = b.AccountID
AND a.LastTransactionDate = b.GiftReceivedDt
WHERE a.FirstTransactionDT between '2015-01-01' and '2015-05-31'
AND b.AppealCode != 2)
This seems to do what you want. FirstTransaction is this year (since it is the first, there can't be previous ones), and appeal code isn't 2. What is the rest of your code trying to do?
Related
I have two tables in my schema. The first contains a list of recurring appointments - default_appointments. The second table is actual_appointments - these can be generated from the defaults or individually created so not linked to any default entry.
Example:
default_appointments
id
day_of_week
user_id
appointment_start_time
appointment_end_time
1
1
1
10:00:00
16:00:00
2
4
1
11:30:00
17:30:00
3
6
5
09:00:00
17:00:00
actual_appointments
id
default_appointment_id
user_id
appointment_start
appointment_end
1
1
1
2021-09-13 10:00:00
2021-09-13 16:00:00
2
NULL
1
2021-09-13 11:30:00
2021-09-13 13:30:00
3
6
5
2021-09-18 09:00:00
2021-09-18 17:00:00
I'm looking to calculate the total minutes that were scheduled in against the total that were actually created/generated. So ultimately I'd end up with a query result with this data:
user_id
appointment_date
total_planned_minutes
total_actual_minutes
1
2021-09-13
360
480
1
2021-09-16
360
0
5
2021-09-18
480
480
What would be the best approach here? Hopefully the above makes sense.
Edit
OK so the default_appointments table contains all appointments that are "standard" and are automatically generated. These are what appointments "should" happen every week. So e.g. ID 1, this appointment should occur between 10am and 4pm every Monday. ID 2 should occur between 11:30am an 5:30pm every Thursday.
The actual_appointments table contains a list of all of the appointments which did actually occur. Basically what happens is a default_appointment will automatically generate itself an instance in the actual_appointments table when initially set up. The corresponding default_appointment_id indicates that it links to a default and has not been changed - therefore the times on both will remain the same. The user is free to change these appointments that have been generated by a default, resulting in setting the default_appointment_id to NULL * - or -* can add new appointments unrelated to a default.
So, if on a Monday (day_of_week = 1) I should normally have a default appointment at 10am - 4pm, the total minutes I should have planned based on the defaults are 360 minutes, regardless of what's in the actual_appointments table, I should be planned for those 360 minutes every Monday without fail. If in the system I say - well actually, I didn't have an appointment from 10am - 4pm and instead change it to 10am - 2pm, actual_appointments table will then contain the actual time for the day, and the actual minutes appointed would be 240 minutes.
What I need is to group each of these by the date and user to understand how much time the user had planned for appointments in the default_appointments table vs how much they actually appointed.
Adjusted based on new detail in the question.
Note: I used day_of_week values compatible with default MySQL behavior, where Monday = 2.
The first CTE term (args) provides the search parameters, start date and number of days. The second CTE term (drange) calculates the dates in the range to allow generation of the scheduled appointments within that range.
allrows combines the scheduled and actual appointments via UNION to prepare for aggregation. There are other ways to set this up.
Finally, we aggregate the results per user_id and date.
The test case:
Working Test Case (Updated)
WITH RECURSIVE args (startdate, days) AS (
SELECT DATE('2021-09-13'), 7
)
, drange (adate, days) AS (
SELECT startdate, days-1 FROM args UNION ALL
SELECT adate + INTERVAL '1' DAY, days-1 FROM drange WHERE days > 0
)
, allrows AS (
SELECT da.user_id
, dr.adate
, ROUND(TIME_TO_SEC(TIMEDIFF(da.appointment_end_time, da.appointment_start_time))/60, 0) AS planned
, 0 AS actual
FROM drange AS dr
JOIN default_appointments AS da
ON da.day_of_week = dayofweek(adate)
UNION
SELECT user_id
, DATE(appointment_start) AS xdate
, 0 AS planned
, TIMESTAMPDIFF(MINUTE, appointment_start, appointment_end)
FROM drange AS dr
JOIN actual_appointments aa
ON DATE(appointment_start) = dr.adate
)
SELECT user_id, adate
, SUM(planned) AS planned
, SUM(actual) AS actual
FROM allrows
GROUP BY adate, user_id
;
Result:
+---------+------------+---------+--------+
| user_id | adate | planned | actual |
+---------+------------+---------+--------+
| 1 | 2021-09-13 | 360 | 480 |
| 1 | 2021-09-16 | 360 | 0 |
| 5 | 2021-09-18 | 480 | 480 |
+---------+------------+---------+--------+
My requirement is to compute the total months and then broken months separately between 2 dates (ie first date from table and second date is current date). If broken months total count is > 15 then account it as one month experience and if its les than 15 don't account that as 1 month experience.
Assume I have a date on table as 25/11/2018 and current date is 06/01/2019;
the full month in between is December, so 1 month experience; and broken months are November and January, so now I have to count the dates which is 6 days in Nov and 6 days in Jan, so 12 days and is <= (lte) 15 so total experience will be rounded to 1 month experience
I referred multiple questions related to calculating date difference in MYSQL from stackoverflow, but couldn't find any possible options. The inbuilt functions in MYSQL TIMESTAMPDIFF, TIMEDIFF, PERIOD_DIFF, DATE_DIFF are not giving my required result as their alogrithms are different from my calculation requirement.
Any clue on how to perform this calculation in MYSQL and arrive its result as part of the SQL statement will be helpful to me. Once this value is arrived, in the same SQL, that value will be validated to be within a given value range.
Including sample table structure & value:
table_name = "user"
id | name | join_date
---------------------
1| Sam | 25-11-2017
2| Moe | 03-04-2017
3| Tim | 04-07-2018
4| Sal | 30-01-2017
5| Joe | 13-08-2018
I wanted to find out the users from above table whose experience is calculated in months based on the aforementioned logic. If those months are between either of following ranges, then those users are fetched for further processing.
table_name: "allowed_exp_range"
starting_exp_months | end_exp_months
-------------------------------------
0 | 6
9 | 24
For ex: Sam's experience till date (10-12-2018) based on my calculation is 12+1 month = 13 months. Since 13 is between 9 & 24, Sam's record is one of the expected output.
I think this query will do what you want. It uses
(YEAR(CURDATE())*12+MONTH(CURDATE()))
- (YEAR(STR_TO_DATE(join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(join_date, '%d-%m-%Y'))) -
- 1
to get the number of whole months of experience for the user,
DAY(LAST_DAY(STR_TO_DATE(join_date, '%d-%m-%Y')))
- DAY(STR_TO_DATE(join_date, '%d-%m-%Y'))
+ 1
to get the number of days in the first month, and
DAY(CURDATE())
to get the number of days in the current month. The two day counts are summed and if the total is > 15, 1 is added to the number of whole months e.g.
SELECT id
, name
, (YEAR(CURDATE())*12+MONTH(CURDATE())) - (YEAR(STR_TO_DATE(join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(join_date, '%d-%m-%Y'))) - 1 -- whole months
+ CASE WHEN DAY(LAST_DAY(STR_TO_DATE(join_date, '%d-%m-%Y'))) - DAY(STR_TO_DATE(join_date, '%d-%m-%Y')) + 1 + DAY(CURDATE()) > 15 THEN 1 ELSE 0 END -- broken month
AS months
FROM user
We can use this expression as a JOIN condition between user and allowed_exp_range to find all users who have experience within a given range:
SELECT u.id
, u.name
, a.starting_exp_months
, a.end_exp_months
FROM user u
JOIN allowed_exp_range a
ON (YEAR(CURDATE())*12+MONTH(CURDATE())) - (YEAR(STR_TO_DATE(u.join_date, '%d-%m-%Y'))*12+MONTH(STR_TO_DATE(u.join_date, '%d-%m-%Y'))) - 1
+ CASE WHEN DAY(LAST_DAY(STR_TO_DATE(u.join_date, '%d-%m-%Y'))) - DAY(STR_TO_DATE(u.join_date, '%d-%m-%Y')) + 1 + DAY(CURDATE()) > 15 THEN 1 ELSE 0 END
BETWEEN a.starting_exp_months AND a.end_exp_months
Output (for your sample data, includes all users as they all fit into one of the experience ranges):
id name starting_exp_months end_exp_months
1 Sam 9 24
2 Moe 9 24
3 Tim 0 6
4 Sal 9 24
5 Joe 0 6
I've created a small demo on dbfiddle which demonstrates the steps in arriving at the result.
I used SQL back in the 90s and starting to get back into it for a project at work.
I want to store departmental budget data so I can run queries and use it in Power Bi.
I have a simple table with columns for the department (fund) and corresponding monthly expense info. I am trying to run a query where I can see the expenses for one fund under the supplies account -- can't get it to work. I know I will probably kick myself when I see the solution so here we go...
Select * FROM expenses
FundID Fund FY Account January February March April May June July August
9999 tester 2019 Grant Assistance to INT'L Individuals 1 2 3 4 5 6 7 8
9999 tester 2019 Grant Assistance to INT'L Organizations 21 22 23 24 25 26 27 28
9999 tester 2019 Grant Assistance to U.S. Individuals 31 32 33 34 35 36 37 38
9999 tester 2019 Grant Assistance to U. S. Organizations 41 42 43 44 45 46 47 48
9999 tester 2019 Salaries, etc. 4500 4500 4500 4500 4500 4500 4500 4500
Table = expenses
Columns
FundID, Fund, FY, Account, January, February… December
SELECT *
FROM expenses;
— results in all data from sample table (as expected)
SELECT *
FROM expenses
WHERE Fund = 'associate1';
— results in all data for ‘associate1’ (as expected)
SELECT *
FROM expenses
WHERE Fund = 'associate1' AND Account = 'Supplies';
— results in alert advising ‘empty result set (i.e. zero rows) (not expected)
—- rows/record is populated
Expected:
FundID Fund FY…. December populated with associate1’s data for supplies.
I also attempted query: SELECT * FROM expenses WHERE Account = ‘Supplies’; (Nothing)
Suppose I have the following data:
TimeTable
Person Week Date EnterTime ExitTime PeriodDiff
----------------------------------------------------
John 1 01.01.2018 09:15 10:35 1:20
John 1 01.01.2018 10:55 12:23 1:28
John 1 01.01.2018 13:00 17:35 4:35
John 1 02.01.2018 09:00 16:35 7:35
John 2 08.01.2018 09:05 11:40 2:35
John 2 08.01.2018 16:15 19:35 3:20
John 2 09.01.2018 10:50 21:57 11:07
I am trying to make a report about the weekly and daily time people were in the company.
So I am trying to create the following report:
+[WeekGroup] +
---------------------------------------------------------------
+[DateGroup] +
WeeklyTotal ------------------------------------------------
Person FirstEntry LastExit TimeInside Period
[PersonGroup] <<Exp3>> Min[EnterTime] Max[ExitTime] <<Exp1>> <<Exp2>>
Exp1 = Max(ExitTime) - Min(EnterTime)
Exp2 = Sum(PeriodDiff)
Exp3 should be Sum(Max(ExitTime) - Min(EnterTime)) for each day
Everything works except for Exp3.
Problem is Exp3 is outside of the DateGroup so the min/max values refer to each of those in the respected week. Also I cannot use the sum of period because of the gaps in time.
How can I get the sum of each weeks calculated time differance? Meaning sum(Exp1) ?
You can use the following expression to get the Max() or Min() inside the group or outside the group, if you add the scope to the aggregate function:
'Returns the max of the whole DataSet
=Min(Fields!ExitTime.Value), "YourDataSetName")
'Returns the max of the group with the name 'YourGroupName'
=Min(Fields!ExitTime.Value), "YourGroupName")
What you also can do, is to reverence to your first expression Expr1() with the following expression (lets assume the first expression is in Textbox1):
=Sum(ReportItems!Textbox1.Value)
I have a table with 10 columns listing results of a marketing campaign including person name, date, residence area, request, and response.
The request column is always a number, indicating the number of survey requests we've sent a specific person.
The response column is a number from 0 to X, representing the number of surveys a person has responded to take.
Now, when I look through the actual table, there are probably around 10% response rate. With lots of non-zero entries in the response column.
However, when I write an aggregate function like this:
SELECT person, date, SUM(requests), SUM(response)
FROM analytics.SurveyResults0304
WHERE group_type = 'Youth'
GROUP BY person, date;
I get a correct number for SUM(requests), but I get a big fat "0" for SUM(response)?
It's the same for all 3 group types. It returns a 0 for SUM(response)
Update: it works fine when I don't include the group_type filter, but how come I can't use it with the WHERE filter?
Thanks!!!
EDIT2: Sample Table
Person Date Group_Type Requests Response Neighborhood FirstName
-------- -------- ----------- -------- -------- ------------ ---------
Nixon 3/3/2013 Youth 3 3 Chinatown Richard
Clinton 3/3/2013 Youth 4 0 Gunhill Bill
Mao 3/3/2013 Youth 5 0 Berryville Chairman
Nixon 3/4/2013 Youth 17 2 Townsford Richard
Gates 3/3/2013 Elderly 41 5 Chinatown Bill
Gates 3/4/2013 Elderly 0 0 Chinatown Bill
Gates 3/5/2013 Elderly 0 0 Chinatown Bill
Gates 3/6/2013 Elderly 0 0 Chinatown Bill
For example
When I do:
SELECT SUM(requests), SUM(response)
FROM analytics.SurveyResults0304
WHERE group_type = 'Youth';
It returns 70 for request, and 0 for response across the board.
Your code seems to be working. See SQL Fiddle built from your data sample.
May be you have extra spaces and/or tabs in your Group_Type column?
PERSON DATE SUM(REQUESTS) SUM(RESPONSE)
--------------------------------------------------------
Clinton March, 03 2013 00:00:00+0000 4 0
Mao March, 03 2013 00:00:00+0000 5 0
Nixon March, 03 2013 00:00:00+0000 3 3
Nixon March, 04 2013 00:00:00+0000 17 2