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

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)

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).

MySql: get MIN record in INNER JOIN If a field is null, or change criteria

I have a query between two tables.
First table is a list of users
+----+-------+-----------+
| id | name | expire_on |
+----+-------+-----------+
| 22 | JOHN | (null) |
| 44 | SMITH | (null) |
| 55 | DOE | 5 |
+----+-------+-----------+
Where "expire_on" can be NULL, but if compiled it is the expire of his subscription, in days.
And I have a list of transactions:
+----+----------------+-----------------+--------------+-------------+----------------------+
| id | id_member_card | amount_original | amount_final | description | utc_date_t |
+----+----------------+-----------------+--------------+-------------+----------------------+
| 1 | 22 | 12 | 12 | (null) | 2017-05-01T10:11:12Z |
| 2 | 22 | 50 | 50 | (null) | 2018-02-01T10:20:30Z |
| 3 | 44 | 7 | 7 | (null) | 2018-02-02T07:50:40Z |
| 4 | 22 | 9 | 9 | (null) | 2018-03-01T10:00:14Z |
| 5 | 44 | 5 | 5 | (null) | 2018-03-03T08:09:10Z |
| 6 | 22 | 0 | 0 | RENEW | 2018-05-02T11:22:33Z |
| 7 | 55 | 12 | 12 | (null) | 2018-05-03T10:20:30Z |
+----+----------------+-----------------+--------------+-------------+----------------------+
I have this starting points:
1) The user "expires" after 365 days of his very first transaction. The id 44 will expire on the 02-02-2019... > BUT >
2) If the user has a field "expire_on", he expires after the X days and not anymore the 365. In my example, id 55 is expired on the 07-05-2018.
3) If in the transaction list there is a RENEW, the user expires 365 days after this transaction renew and not anymore from the first one. Id 22 will expire only on the 02-05-2019 (pratically, we could consider a RENEW transaction as his first_transaction, if this can help to write a smarter query ) > BUT
If the user has the expire_on set, he expires X days after this renew (if the id 22 had expire_on set on, for example, let's say, 10 days, he would be expired on 12-05-2018 and not anymore 02-05-2019).
I hope that I'm clear.
Now MySql query, that I cannot complete considering the RENEW or not.
First of all, this is the link to the fiddle: http://sqlfiddle.com/#!9/16a3a/1
And this is the query:
SELECT member_card.id AS id,
member_card.name,
member_card.expire_on,
ts1.* FROM member_card
INNER JOIN (
SELECT member_card.id,
MIN(transaction.utc_date_t) AS first_transaction,
MAX(transaction.utc_date_t) AS last_transaction,
IFNULL (
DATE(DATE_ADD(MAX(transaction.utc_date_t), INTERVAL expire_on DAY)) ,
DATE(DATE_ADD(MAX(transaction.utc_date_t), INTERVAL 365 DAY))
)
AS final_expire ,
SUM(transaction.amount_final) AS balance
FROM transaction
INNER JOIN member_card ON transaction.id_member_card = member_card.id
GROUP BY member_card.id ) AS ts1 ON member_card.id = ts1.id
WHERE ( final_expire BETWEEN '2019-02-01' AND '2019-02-28' )
GROUP BY member_card.id
With my query, I would expect to find id 44, because his first transaction is made on 2018-02-01, so he will expire on the february 2019. But my query considers only LAST transaction (see MAX aggregate).
So, I need to search and looking for:
If exists a RENEW:
If yes, take this date and sum 365 (OR the custom expire date)
If no, take MIN transaction.
Thank you very much for your support.
Tryng to solve
I could also get the last renew transactions, with another query:
SELECT id_member_card , MAX(utc_date_t) AS last_transaction_renew
FROM transaction
WHERE description IS NOT NULL
GROUP BY id_member_card
and substitute these found id_member_card to the others, using this last_transaction_renew inside that IFNULL, but, how?

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.

SELECT all the latest non null records available based on date range in another table

Following on from this question SELECT all the newest record distinct keyword with a non null value in one column
I now have a problem where I have this data
PRODUCT:
id| product | amount| ownershipid
1 | ipod | 200 | 2
2 | ipod | 250 | 3
3 | ipod | 150 | 4
4 | apple | 100 | 1
5 | apple | 98 | 2
6 | apple | 500 | 3
7 | itunes | NULL | 1
8 | itunes | 50 | 2
9 | itunes | NULL | 3
10 | itunes | NULL | 4
OWNERSHIP:
ownershipid| start | end
1 | 2011-01-01 | 2011-12-31
2 | 2012-01-01 | 2012-12-31
3 | 2014-01-01 | 2014-12-31
4 | 2013-01-01 | 2013-12-31
I need the most recent amount available for each product. I can not do an order by on ownershipId as the most recent data is from 2014. not from 2013. OwnershipId is Autoincrement and we accept historic data.
So, my result should return rows 2, 6 and 8.
Assuming most recent is defined by end, you can do a somewhat convoluted join to get the result;
SELECT p.product, p.amount
FROM product p JOIN ownership o ON p.ownershipid = o.ownershipid
JOIN (
SELECT p.product, MAX(o.end) end
FROM product p JOIN ownership o ON p.ownershipid = o.ownershipid
WHERE p.amount IS NOT NULL GROUP BY p.product) z
ON p.product = z.product AND o.end = z.end;
An SQLfiddle to test with.
The inner query gets the max date that has a non null amount per item.
The outer query gets the amount for that time/product.

MYSQL - Show sum of total work_time but only display results from last updated date_time

Sorry for the confusing Title. I have been struggling with this query for quite a long time and I will do my best to explain what I am looking for.
I have 3 tables I am trying to pull from. We will call them:
headers
details
users
The headers table contains two important fields:
ref_num
headers_uid
The details table has the following important rows:
details_uid
headers_uid
work_time
user_uid
disposition
date_time
The users table has the following:
user_uid
username
An example of the details table which contains the majority of the information I need is as follows:
details_uid | headers_uid | work_time | user_uid | disposition | date_time
1 | 10 | 25:00 | 5 | o | 2013-07-02 12:14:48
2 | 10 | 10:00 | 7 | p | 2013-07-02 13:55:37
3 | 10 | 5:00 | 5 | c | 2013-07-02 15:04:28
4 | 12 | 7:00 | 5 | o | 2013-07-02 15:20:21
5 | 12 | 12:00 | 7 | p | 2013-07-02 15:35:27
6 | 12 | 3:00 | 7 | c | 2013-07-02 15:40:19
What I'm trying to do is display the headers.refnum, sum of total work_time for the unique user for ALL details.details_uids with the same details.headers_uid and only the LAST disposition of the details.headers_uid for the each user. The results must look for a specific date_time (I generally search by > CURDATE() to grab events for today) Also, instead of displaying the user_uid, I will be searching within a WHERE clause by users.username (I have usernames stored in a txt file which is turned into an IN statement).
Ideally, this is what I would like to see:
ref_num | work_time | username | disposition |
A10 | 30:00 | mike | c |
A10 | 10:00 | james | p |
A12 | 7:00 | mike | o |
A12 | 15:00 | james | c |
Any help is greatly appreciated! I know this will probably involve a good deal of join statements and subqueries and I've been banging my head on the table trying to get it right. I know this would be much easier using php, but sadly, I don't have php access at work yet (don't ask..)
I think this does what you want:
select h.ref_num, sum(d.work_time), u.username, d.disposition
from details d join
headers h
on d.headers_uid = h.headers_uid join
users u
on d.user_uid = u.user_uid
where d.disposition = (select disposition
from details d2
where d2.headers_uid = d.headers_uid and
d2.users_uid = d.users_uid
order by date_time desc
limit 1
)
group by h.ref_num, u.username, d.disposition;
The key is the where clause that selects the last disposition for a given set of details records.