Count the different dates of each users mysql - mysql

I have a table called order_match which contain columns createdby as the buyer, createdAt as the date of transaction, and order_status_Id as the order_status.
If order_status_id is (4, 5, 6, 8) then the transaction are approved, so I want to count every different days of each buyer so I know the gap of each buyer to doing new transaction after the last transaction.
After that, I can determine the max day of the longest transaction gap, and the minimum day, and the average of the buyers on that range to doing transaction
This is the example data of the transaction between 2018-12-01 until 2018-12-04
+-----------+------------+-----------------+
| createdby | createdAt | order_status_id |
+-----------+------------+-----------------+
| A | 2018-12-01 | 4 |
| A | 2018-12-02 | 5 |
| A | 2018-12-04 | 5 |
| B | 2018-12-02 | 5 |
| B | 2018-12-04 | 5 |
| C | 2018-12-03 | 5 |
| C | 2018-12-04 | 6 |
+-----------+------------+-----------------+
From that data, what expected results is :
+-------------------------------+---------+---------------------------------------+
| max day | min day | average day of the transaction |
+-------------------------------+---------+---------------------------------------+
| 2 (the transaction on | 1 | 1,5 (average from each gap |
| buyer "A" ('2018-12-02' to | | day / of the gap transaction held) |
| '2018-12-04) and "B" | | |
| ('2018-12-02' to '2018-12-04) | | |
+-------------------------------+---------+---------------------------------------+
The point is I want to count time lag in days between every two consecutive purchases made by the buyer within the selected time frame. the difference in days also calculates the time lag between the last purchase before the selected time range (if any) and the first purchase in the selected time range.
This is my progress syntax
SELECT
SUM((DATEDIFF(GREATEST(om1.createdAt, '2018-12-18'),
LEAST(om2.createdAt, '2018-12-20')) * -1) + 1)
FROM order_match om1, order_match om2
WHERE om1.createdAt <='2018-12-20'
AND om2.createdAt >= '2018-12-18'
AND om1.order_status_Id IN (4, 5, 6, 8)
GROUP BY om1.createdby;

Related

MySQL: Create running sum for all dates in range?

I have a table Service, that stores records of sales and referral types that led to the sale. I need to know the total number of sales that resulted from a given referral type over a range of dates. The relevant data in Service looks like the following:
+------+-------+------------+
| uuid | sr_id | s_saledate |
+------+-------+------------+
| | 1 | 2020-01-01 |
| | 1 | 2020-01-01 |
| | 2 | 2021-01-01 |
| | 2 | 2021-01-01 |
| | 1 | 2021-01-01 |
+------+-------+------------+
I want to count the number of sales for each referral type (sr_id) in a given date range.
If my date range is 2020-01-01 thru 2021-01-01, my output should be something like:
+------+-------+------------+----------------------+
| uuid | sr_id | date | num_sales_as_of_date
+------+-------+-----------------------------------+
| | 1 | 2020-01-01 | 2 |
| | 1 | 2020-01-02 | 2 |
| | 1 | 2020-01-03 | 2 |
........................................................ < many rows for days in range
1 2021-01-01 | 3
| | 2 | 2020-01-01 | 0 |
| | 2 | 2020-01-02 | 0 |
........................................................ < many rows for days in range
| | 2 | 2020-01-01 | 2 |
+------+-------+-----------------------------------+
There should be a row for each referral type on each date in the range.
Right now my query looks like:
SELECT s.sr_id,
s.s_saledate AS date,
Count(s.uuid)
OVER (
partition BY s.sr_id
ORDER BY s.s_saledate) AS num_sales_as_of_date
FROM Service s
How do I get the running sum for each referral type on days that had no Service with that particular referral type id?
*** EDIT FOR CLARIFICATION***
For example, in the first table I give there is no Service row in the Service table with sr_id = '1' AND s_saledate === "2020-01-02". There were two rows from prior days where sr_id = '1'. (2020-01-01). My output row for "2020-01-02" is:
sr_id date num_sales_as_of_date
1 | 2020-01-02 | 2 |
You need to left join your Services table from a table with all the dates in the range and a table with all the referral types, so that you get a row with every combination of date and referral type:
WITH RECURSIVE dates AS (
SELECT date('2020-01-01') AS date
UNION ALL
SELECT dates.date + INTERVAL 1 DAY
FROM dates
WHERE dates.date <= '2020-01-05'
)
SELECT ServiceReferral.sr_id,
dates.date,
Count(s.uuid)
OVER (
partition BY ServiceReferral.sr_id
ORDER BY dates.date) AS num_sales_as_of_date
FROM dates
CROSS JOIN ServiceReferral
LEFT JOIN Service s ON s.s_saledate=dates.date AND s.sr_id=ServiceReferral.sr_id
fiddle
If you do this a lot, it may be more convenient to create an actual table dates with all the dates from 0000-01-01 to 9999-12-31 and use that instead (selecting dates in the desired range in the where clause).

How to select rows with the latest date and calculate another field based on the row

I have two tables i.e vehicle and vehicle_maintenance.
vehicle
-----------------------------------
| v_id | v_name | v_no |
-----------------------------------
| 1 | car1 | car123 |
-----------------------------------
| 2 | car2 | car456 |
-----------------------------------
vehicle_maintenance
-----------------------------------------------------------------------
| v_main_id | v_id | v_main_date | v_main_remainder |
-----------------------------------------------------------------------
| 1 | 1 | 2020/10/10 | 1 |
| 2 | 1 | 2020/10/20 | 2 |
| 3 | 2 | 2020/10/04 | 365 |
| 4 | 2 | 2020/10/15 | 5 |
-----------------------------------------------------------------------
I want to get each car maintenance details i.e car2 maintenance date is 2020/10/15 and i want to check next maintenance date based on v_main_remainder field. That means next maintenance date will be 2020/10/20 ( add 5 day to the maintenance date). I want to also calculate the no of days left from next maintenance date. Suppose today is 2020/10/10 then it will show 10 days left.
Here is my query
SELECT
v.v_id,
v.v_name,
v.v_no,
max(vm.v_main_date) as renewal_date,
datediff(
DATE_ADD(
max(vm.v_main_date), INTERVAL +vm.v_main_remainder day
),
now()
) as day_left
FROM vehicle as v, vehicle_maintenance as vm
GROUP BY v.v_id
But the problem is vm.v_main_remainder in date_add function taken from first row.
Here is the result
-----------------------------------------------------------------------
| v_id | v_name | v_no | renewal_date | day_left |
-----------------------------------------------------------------------
| 1 | car1 | car123 | 2020/10/20 | 11 |
-----------------------------------------------------------------------
| 2 | car2 | car456 | 2020/10/15 | 370 |
-----------------------------------------------------------------------
As a starter, your query is obviously missing a join condition between the two tables, so that's a cartesian product. This type of problem is much easier to spot when using explicit joins.
Then: you want to filter on the latest maintenance record per car, so aggregation is not appropriate.
One option uses window functions, available in MySQL 8.0:
select v.v_id, v.v_name, v.v_no, vm.v_main_date as renewal_date,
datediff(vm.v_main_date + interval vm.v_main_remainder day, current_date) as day_left
from vehicle as v
inner join (
select vm.*, row_number() over(partition by v_id order by v_main_date desc) rn
from vehicle_maintenance
) as vm on vm.v_id = v.v_id
where vm.rn = 1
Note that I changed now() to current_date, so datediff() works consistently on dates rather than datetimes.

Sql check a value in column is greater than number of rows

i am building a web app where customer can book a service from a business(vendor). but first vendor need to add
what services he provide, where he able to deliver those services, and from which time (start time) to which time(end time) how many users is available
so i am confusing that suppose a customer book a service for some particular time and when another customer book a service from that vendor we need to calculate is the number of users available
in this time period is greater than count of already booking (which are not completed yet) in that particular time
i have these three table
users = (for both customer & vendors)
id | name | address | role_id
1 | Customer | some address | 1
2 | Vendor | Some Address | 2
3 | Another Customer | Some address | 1
4 | Another Vendor | address | 2
vendor_available_time = (only for vendor)
id | date_start_time | date_end_time | no_of_users |vendor_id
1 | 2019-10-16 00:00:00 | 2019-19-16 23:59:59 | 3 | 2
1 | 2019-10-16 09:00:00 | 2019-19-16 17:00:00 | 1 | 4
1 | 2019-10-17 00:00:00 | 2019-19-17 23:59:59 | 3 | 2
1 | 2019-10-17 09:00:00 | 2019-19-17 17:00:00 | 2 | 4
bookings = (for both)
id | booking_status | booking_datetime | customer_id | vendor_id
1 | job started | 2019-10-16 10:00:00 | 1 | 2
1 | completed | 2019-10-15 10:00:00 | 1 | 2
1 | accepted | 2019-10-16 09:00:00 | 3 | 2
1 | work in progress | 2019-10-16 09:00:00 | 3 | 2
please help me with a query
where i can get all vendors whose no_of_users is greater than count(bookings) where booking_datetime >= date_start_time and booking_datetime < date_end_time and booking_status is not in ['completed', 'rejected', 'cancelled']
Any suggestion will be really appreciated can i get those result from one query, should i use multiple queries or should i change my database structure
The output i want is just a user_ids whose are available for that time period and whose no of users is greater than already count of bookings for that time
Is this what you want?
Select b.vendor_id from
bookings b join vendor_available_time v on b.vendor_id=v.id
where b.booking_datetime >= v.date_start_time and
b.booking_datetime < v.date_end_time
and b.booking_status not in ['completed', 'rejected', 'cancelled']
group by b.vendor_id having count(distinct b.id) <=count(b.customer_id)

subtract 2 DB rows from each other for each unique partner_ID? MYSQL

I have a MYSQL table where I need to get to subtract values from 2 different rows.
This is my DB table:
Tablename: ext_partnertotals
| Partner_ID | Partnername | Month | Year | Total |
|------------|-------------|-------|------|-------|
| 1 | Partner 1 | 1 | 2018 | 10 |
| 1 | Partner 1 | 2 | 2018 | 12 |
| 2 | Partner 2 | 1 | 2018 | 18 |
| 2 | Partner 2 | 2 | 2018 | 12 |
It should get this with a query:
| Partner_ID | Partnername | up/down |
|------------|-------------|---------|
| 1 | Partner 1 | +2 |
| 2 | Partner 2 | -6 |
I need to get the Subtract value of 2 different months for each Partner.
Every Partner has a tablerow for each month and a value for that month.
Now I need to get If they went up or went down in value since the month before.
Can someone write me a query?
Since you're unable to improve your terrible schema, I recommend you use a (very ugly/hard to maintain and very slow) correlated subquery:
SELECT Partner_ID, Partnername, Year, Month, Total - (
SELECT Total
FROM ext_partnertotals AS prev
WHERE prev.Partner_ID = cur.Partner_ID AND CASE cur.Month
WHEN 1 THEN prev.Year = cur.Year - 1 AND prev.Month = 12
ELSE prev.Year = cur.Year AND prev.Month = cur.Month - 1
END
) AS `up/down` FROM ext_partnertotals AS cur
See it on sqlfiddle.

Average on group totals i Reporting Services 2012

I am trying to make an average on group totals in SQL Server Reporting Services 2012 with a tablix.
Seems to be the same problem that this question is struggeling with...however there are no responses: SQL 2005 Reporting Services - Wrong Average Values
My tablix looks like the following:
+-------+---------+-----------+-----------+
| | | Product A | Product B |
+-------+---------+-----------+-----------+
| | Average | ??? | ??? |
| | | | |
| Week1 | | 550 | 175 |
| | Day 1 | 250 | 100 |
| | Day 2 | 200 | 50 |
| | Day 3 | 100 | 25 |
| | | | |
| Week2 | | 600 | 240 |
| | Day 1 | 300 | 200 |
| | Day 2 | | 30 |
| | Day 3 | | 10 |
+-------+---------+-----------+-----------+
The average should only be calculated for the week-totals. I.e. for Product A the average should be (550+600)/2=575.
My formula for average looks like the following:
=Avg(Fields!WeekTotal.Value)
however this gives a wrong result - my guess is that it takes the days into account aswell?
The question gets harder, because sometimes i do not have the Days sale, in which case the WeekTotal will be a prediction
My dataset, which comes from a SQL Server table, contains the following data:
+---------+------+-----+-----------+----------+
| Product | Week | Day | WeekTotal | DayTotal |
+---------+------+-----+-----------+----------+
| A | 1 | 1 | 550 | 250 |
| A | 1 | 2 | 550 | 200 |
| A | 1 | 3 | 550 | 100 |
| B | 1 | 1 | 175 | 100 |
| B | 1 | 2 | 175 | 50 |
| B | 1 | 3 | 175 | 25 |
| A | 2 | 1 | 600 | 300 |
| A | 2 | 2 | 600 | NULL |
| A | 2 | 3 | 600 | NULL |
| … | … | … | … | … |
+---------+------+-----+-----------+----------+
Any help is appreciated!
You can simply sum all the detail values (at day level) then divide by the number of unique weeks. Something like this...
=Sum(Fields!DayTotal.Value)/CountDistinct(Fields!Week.Value)
In your sample CountDistinct(Fields!Week.Value) will return 2
This all assumes you have a simple Matrix as your report design something like this...
The expression is the expression above to calculate the average.
Please note in my sample data I have more detail so I grouped by day as well as week. In your case you don;t need to group by day (but it will still work if you do)
Also, you don't need the week total as you can simply sum the day totals at the week group level.
UPDATE based on OP comments:
As you need to use the actual week totals in your data (which are not always just a sum of the day totals as I thought previously) we will have to edit the dataset and do a bit of the work there.
Below is the code is used to generate your sample dataset.
DECLARE #t TABLE (Product varchar(1), [Week] int, [Day] int, WeekTotal int, DayTotal[int])
INSERT INTO #t
VALUES
('A', 1, 1, 550, 250),
('A', 1, 2, 550, 200),
('A', 1, 3, 550, 100),
('B', 1, 1, 175, 100),
('B', 1, 2, 175, 50),
('B', 1, 3, 175, 25),
('A', 2, 1, 600, 300),
('A', 2, 2, 600, NULL),
('A', 2, 3, 600, NULL)
SELECT
*
, CAST(WeekTotal as float) / COUNT(*) OVER(PARTITION BY Product, [Week]) as WeekTotalAverage
FROM #t
Notice that I now return an extra column WeekTotalAverage (I know, poor name!). What this does is give us an average per product/week of the weektotal, we can then simply replace the daytotal in the previous expression with this field. The dataset output looks like this.
Product Week Day WeekTotal DayTotal WeekTotalAverage
A 1 1 550 250 183.333333333333
A 1 2 550 200 183.333333333333
A 1 3 550 100 183.333333333333
A 2 1 600 300 200
A 2 2 600 NULL 200
A 2 3 600 NULL 200
B 1 1 175 100 58.3333333333333
B 1 2 175 50 58.3333333333333
B 1 3 175 25 58.3333333333333
I've CAST the result of this column to a float or we end up losing fractions of the numbers when the result is recurring. So in this case product B week 1 has three entries so we take the week total (165) and divide by 3 to give us 58.33 recurring.
The report design changes a little too. It now looks like this.
The week total is now just [WeekTotal] (previously it was [SUM(DayTotal)]) and the expression for the Average is now..
=Sum(Fields!WeekTotalAverage.Value) / CountDistinct(Fields!Week.Value)
In the matrix, there are two row groups, the first groups by Week and the second (child group) is grouped by Day. The column group is grouped on Product.
My solution would be to create a second recordset that has only one weekly total entry for every week, and then take the average from that scope rather than the recordset used for the detailed part of the report.