SELECT the SUM of multiple values to which user is subscibed - mysql

Table: assignments
=======================
|customerid |tariffid |
=======================
| 1 | 2 |
| 2 | 2 |
| 1 | 4 |
| 3 | 4 |
=======================
Table: cash
=======================
|customerid | value |
=======================
| 1 | 2 |
| 1 | 9 |
| 1 | -15 |
| 2 | -9 |
| 2 | 2 |
| 2 | 2 |
| 2 | -9 |
| 3 | 9 |
=======================
Table: customers
=================================================
| id | lastname| name | cutoffstop | deleted |
=================================================
| 1 | Doe | John | 10 | 0 |
| 2 | Foo | Jack | 10 | 0 |
| 3 | Zoo | Jenny| 20 | 0 |
| 4 | Boo | Jane | 5 | 0 |
=================================================
Table: tariffs
================
| id | value|
================
| 1 | 0 |
| 2 | 2 |
| 3 | 0 |
| 4 | 9 |
================
I have four tables. Also I have two queries that work, but I need to merge the queries somehow.
The first query gives me a row of customerid(id) - lastname - name - balance[=sum(cash.value)]
SELECT customers.id AS id, UPPER(lastname) AS lastname, name, SUM(cash.value) AS balance
FROM customers
JOIN cash ON customers.id = cash.customerid
WHERE deleted = 0 AND cutoffstop < 50
GROUP BY customers.id, lastname, name
HAVING SUM(cash.value) < $limit
ORDER BY lastname, name
Example resulting row first query:
id lastname name balance
1 DOE John -4 (=2+9+-15)
The second query gives me a row of customerid(id) - maxdept [=sum(tariffs.value)]
SELECT SUM(tariffs.value) AS maxdebt, customers.id AS id
FROM tariffs
INNER JOIN assignments ON tariffs.id = assignments.tariffid
INNER JOIN customers ON assignments.customerid = customers.id
GROUP BY id
Example resulting row second query:
id maxdept
1 11 (=9+2)
Note: maxdept = $limit
Now, what I actually want is a a combined query where $limit in the first query IS the outcome of the second
query -> the sum of tariffs.value (=maxdept) per customerid. (Now the $limit is statically defined in a config-file.)
Thanks in advance!

SELECT a.id, a.lastname, a.name, a.balance, b.maxdebt
FROM (SELECT customers.id AS id, UPPER(lastname) AS lastname, name, SUM(cash.value) AS balance
FROM customers
JOIN cash ON customers.id = cash.customerid
WHERE deleted = 0 AND cutoffstop < 50
GROUP BY customers.id, lastname, name) a
INNER JOIN (SELECT SUM(tariffs.value) AS maxdebt, customers.id AS id
FROM tariffs
INNER JOIN assignments ON tariffs.id = assignments.tariffid
INNER JOIN customers ON assignments.customerid = customers.id
GROUP BY id ) b ON a.id = b.id and a.balance < b.maxdebt

Related

query to get customer list using JOIN with sum () of the amounts spent in orders

I have the following tables
table anag (customer registry)
id | surname | name | phone |
----------------------------------------------
1 | Brown | Jack | +3989265781 |
2 | Smith | Bill | +3954872358 |
3 | Rogers | Stan | +3912568453 |
4 | Pickford | Eric | +3948521358 |
----------------------------------------------
table levels (table that connects each customer to his salesperson. For database registration reasons, the link between customer and seller is given by the customer's telephone number)
id | client_phone | id_seller |
--------------------------------------
1 | +3989265781 | 4 |
2 | +3954872358 | 7 |
3 | +3912568453 | 7 |
4 | +3948521358 | 8 |
--------------------------------------
table orders (contains all purchases made by customers, of course)
id | id_client | id_item | id_seller | price | status |
--------------------------------------------------------------------
1 | 1 | 2 | 4 | 12.50 | 2 |
2 | 2 | 2 | 7 | 12.50 | 2 |
3 | 2 | 3 | 7 | 10.00 | 3 |
4 | 2 | 3 | 7 | 10.00 | 3 |
5 | 2 | 4 | 7 | 20.50 | 1 |
6 | 3 | 2 | 7 | 12.50 | 1 |
7 | 3 | 5 | 7 | 19.00 | 3 |
8 | 3 | 7 | 7 | 31.00 | 2 |
9 | 4 | 1 | 8 | 5.00 | 1 |
--------------------------------------------------------------------
What I'm trying to do is get from the JOIN of these tables a complete list by seller of his customers sorted in descending order by the amount spent on orders as long as the order status is 2 or 3
Something like this (example seller id 7):
id | surname | name | amaount |
----------------------------------------
3 | Rogers | Stan | 50.00 |
2 | Smith | Bill | 32.50 |
----------------------------------------
I have tried with this query which seems correct to me, but unfortunately it returns me error in fetch_assoc()
SELECT a.id, a.surname, a.name, o.amount FROM levels AS l
JOIN anag AS a ON a.phone = l.client_phone
JOIN {
SELECT id_client, SUM(price) AS amount FROM orders
WHERE id_seller = '7' AND (status = '2' OR status = '3') GROUP BY id_client
} AS o ON o.id_client = a.id
WHERE l.id_seller = '7'
ORDER BY o.amount DESC
If I separate the subquery from the main query, both return the data I expect and it seems strange to me the JOIN between the two does not work properly
I think the only real error is using curly braces instead of parentheses:
SELECT a.id, a.surname, a.name, o.amount
FROM levels l JOIN
anag a
ON a.phone = l.client_phone JOIN
(SELECT id_client, SUM(price) AS amount
FROM orders
WHERE id_seller = '7' AND status IN ('2', '3'))
GROUP BY id_client
) o
ON o.id_client = a.id
WHERE l.id_seller = '7'
ORDER BY o.amount DESC;
In addition:
You can use IN to shorten an equality comparison to multiple values.
Although I left them in, status and id_seller look like numbers. If so, drop the single quotes. Don't mix data types.
Your question is ambiguous on what to do if the seller in orders differs from the seller in anag for a customer. This keeps your logic (the sellers need to match).
SELECT a.id, a.surname, a.name, sum(o.price) 'amount'
FROM anag a
LEFT JOIN levels l ON l.id =a.id
LEFT JOIN orders of ON o.id_seller = l.id_seller AND o.id_client = l.id
GROUP BY o.id_seller
ORDER BY amount DESC

SQL Query: join with condition

I have the following tables:
Customer
| c_id | name |
| -------- | -------------- |
| 1 | Adam |
| 2 | Bradley |
| 3 | Chandler |
| 4 | Damian |
| 5 | Eric |
| 6 | Frank |
orders
| order_id | c_id | amount
| -------- | -------------- | -------------- |
| 1 | 1 | 50
| 2 | 1 | 2
| 3 | 2 | 15
| 4 | 2 | 22
| 5 | 2 | 10
| 6 | 2 | 7
| 7 | 3 | 7
| 8 | 3 | 2
| 9 | 5 | 18
| 10 | 5 | 24
| 11 | 6 | 60
| 12 | 6 | 1
I want to create a list of users who have order amounts over 50.
This list should include c_id, name and the sum of all their orders including those under 50.
so it should look like this:
| c_id | name | amount
| -------- | -------------- | -------------- |
| 1 |Adam | 52
| 6 | Frank | 61
You can use group by and having:
select c.c_id, c.name, sum(o.amount)
from orders o join
customers c
on o.c_id = c.c_id
group by c.c_id, c.name
having max(o.amount) > 50;
SELECT
c_id
, name
, SUM(amount) AS total_amount
FROM
orders a
INNER JOIN customer b
ON b.c_id = a.user_id
WHERE
c_id IN (
SELECT
user_id
FROM
orders
WHERE
amount >= 50)
GROUP BY c_id, name
Best to break this down into chunks:
Customers who have a total amount over 50:
SELECT user_id FROM orders GROUP BY user_id HAVING sum(amount) >= 50;
Sum of the amounts for each order for customers that meet the criteria above:
SELECT user_id, sum(amount) as order_total
FROM orders
WHERE user_id IN (SELECT user_id FROM orders HAVING sum(amount) >= 50 GROUP BY user_id)
GROUP BY user_id;
You can just join over to your customer table to grab the name. Didn't include since that is the more straightforward ask here.

How to join from 3 table with condition

I have 3 table inside my database, my first table is person table its store all person name, my second table is hobby table its store all hobby of all person,the third table is referensi table store all references person and hobby.
Tabel person : Tabel hobby : Tabel referensi :
----------------- ------------------ -------------------------------------
| id | name | | id | hobby | | id | ref_person | ref_hobby |
----------------- ------------------ -------------------------------------
| 1 | Rose | | 1 | Makan | | 1 | 1 | 1 |
| 2 | Lisa | | 2 | Renang | | 2 | 1 | 3 |
| 3 | Jisoo | | 3 | Nyanyi | | 3 | 1 | 4 |
| 4 | Jennie| | 4 | Youtube| | 4 | 3 | 5 |
----------------- | 5 | Masak | -------------------------------------
------------------
I want to count all hobby by that person
Example I want select Rose : Or I want select Jisoo :
--------------------------- ---------------------------
| id | hobby | count | | id | hobby | count |
--------------------------- ---------------------------
| 1 | Makan | 1 | | 1 | Makan | 0 |
| 2 | Renang | 0 | | 2 | Renang | 0 |
| 3 | Nyanyi | 1 | | 3 | Nyanyi | 0 |
| 4 | Youtube| 1 | | 4 | Youtube| 0 |
| 5 | Masak | 0 | | 5 | Masak | 1 |
--------------------------- ---------------------------
And so forth, how do I solve this problem?
This is my query that I write but doesn't seem to work, because only data with count greater than 0 is shown.
SELECT
hobby.id,
hobby.name,
count( referensi.id ) AS count
FROM
referensi
LEFT OUTER JOIN hobby ON hobby.id = referensi.ref_hobby
JOIN person ON referensi.ref_person = person.id
WHERE person.id = 1
GROUP BY
hobby.id
Thanks in advance.
To solve this you need to JOIN referensi to person, selecting only entries in referensi corresponding to the person of interest, and then RIGHT JOIN to hobby. If there is no matching entry, the output is 0, otherwise 1. For example, for person 1:
SELECT h.id,
h.hobby,
CASE WHEN r.id IS NULL THEN 0 ELSE 1 END AS count
FROM referensi r
JOIN person p ON p.id = r.ref_person AND p.id = 1
RIGHT JOIN hobby h ON h.id = r.ref_hobby
ORDER BY h.id
This can also be implemented with a correlated subquery:
SELECT h.id,
h.hobby,
EXISTS (SELECT * FROM referensi r WHERE r.ref_hobby = h.id AND r.ref_person = 1) AS count
FROM hobby h
If a person/hobby tuple can appear in the referensi table more than once, you do need to do a COUNT:
SELECT h.id,
h.hobby,
COUNT(r.id) AS count
FROM referensi r
JOIN person p ON p.id = r.ref_person AND p.id = 1
RIGHT JOIN hobby h ON h.id = r.ref_hobby
GROUP BY h.id
Output (for all three queries on your sample data):
id hobby count
1 Makan 1
2 Renang 0
3 Nyanyi 1
4 Youtube 1
5 Masak 0
Demo on SQLFiddle
You can try to use condition aggravate function with OUTER JOIN
setting condition in CASE WHEN
Query 1:
SELECT
hobby.id,
hobby.name,
COUNT(CASE WHEN person.id = 3 THEN 1 END) AS count
FROM
hobby
LEFT JOIN referensi ON hobby.id = referensi.ref_hobby
LEFT JOIN person ON referensi.ref_person = person.id
GROUP BY
hobby.id,
hobby.name
Results:
| id | name | count |
|----|---------|-------|
| 1 | Makan | 0 |
| 2 | Renang | 0 |
| 3 | Nyanyi | 0 |
| 4 | Youtube | 0 |
| 5 | Masak | 1 |
You want to start your joining from hobby table, and use LEFT JOINs to optionnaly bring up the matching records in other tables.
SELECT
h.id,
h.hobby,
count( p.id ) AS count
FROM
hobby h
LEFT JOIN referensi r ON h.id = r.ref_hobby
LEFT JOIN person p ON r.ref_person = p.id AND p.id = 1
WHERE p.name is NULL OR p.name = 'Rose'
GROUP BY h.id, h.hobby
It is also a good practice to use table aliases, I added them to your query.
Demo on DB Fiddle for user Rose :
| id | hobby | count |
| --- | ------- | ----- |
| 1 | Makan | 1 |
| 2 | Renang | 0 |
| 3 | Nyanyi | 1 |
| 4 | Youtube | 1 |
| 5 | Masak | 0 |

MySQL return distinct accounts from one table, average rating from another, all based on service area in another table

as the title states, I'm trying to return a query which gets account details from the accounts table, gets the average rating for the account from the reviews table, and limits the rows to the service location associated to the account.
Here are the simplified tables:
accounts
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | John | Smith |
| 2 | Bob | Doe |
| 3 | Alice | McLovin |
| 4 | Bruce | Wayne |
+----+------------+-----------+
reviews
+----+-------------+-----+--------+
| id | acccount_id | ... | rating |
+----+-------------+-----+--------+
| 1 | 1 | ... | 9 |
| 2 | 1 | ... | 10 |
| 3 | 2 | ... | 7 |
| 4 | 1 | ... | 2 |
| 5 | 4 | ... | 6 |
+----+-------------+-----+--------+
service_area
+----+-------------+---------+
| id | acccount_id | city_id |
+----+-------------+---------+
| 1 | 1 | 1140 |
| 2 | 1 | 1001 |
| 3 | 2 | 1140 |
| 4 | 1 | 1086 |
| 5 | 4 | 1001 |
+----+-------------+---------+
For example, the user may request to view all accounts which have a service area of city_id 1140. The query should then return the first_name, last_name, and average rating for each account within the specified service area. Note that accounts can have multiple service areas (see service_area table).
Thanks in advance!
UPDATE:
The following QUERY did the trick! I needed a LEFT JOIN for the reviews table!
SELECT a.first_name, a.last_name, AVG(r.rating) avg_rating
FROM accounts a
JOIN service_area sa
ON a.id = sa.account_id AND sa.city_id = 1140
LEFT JOIN reviews r
ON a.id = r.account_id
GROUP BY a.id
You can use joins and simple aggregation with group by
SELECT a.*,
AVG(r.rating) avg_rating
FROM accounts a
JOIN reviews r ON a.id = r.acccount_id
JOIN service_area s ON a.id = s.acccount_id
WHERE s.city_id = 1140
GROUP BY a.id
Result set will be like
id first_name last_name avg_rating
------ ---------- --------- ------------
1 John Smith 7.0000
2 Bob Doe 7.0000
Use LEFT join for when there are no reviews available
SELECT a.*,
COALESCE(AVG(r.rating),0) avg_rating
FROM accounts a
LEFT JOIN reviews r ON a.id = r.acccount_id
JOIN service_area s ON a.id = s.acccount_id
WHERE s.city_id = 1140
GROUP BY a.id
DEMO

show alternating rows in mysql

Let's say I have these tables:
tblPerson
id | name |
1 | A |
2 | B |
3 | C |
tblB
id | personId | loan |
1 | 1 | 100 |
2 | 1 | 50 |
3 | 2 | 25 |
tblC
id | personId | payment |
1 | 1 | 20 |
2 | 1 | 10 |
How do I produce this output:
Output
id | name | loan | payment | balance |
1 | A | 100 | 0 | 100 |
1 | A | 0 | 20 | 80 |
1 | A | 50 | 0 | 130 |
1 | A | 0 | 10 | 120 |
2 | B | 25 | 0 | 25 |
I need the output to sbow the loan first then the payment the loan again and so on.
This sort of query (http://sqlfiddle.com/#!2/759df4/3/0) will generate an interleaved set of loans and payments.
SELECT id,name,loan,payment
FROM (
SELECT p.id id,
p.name name,
0 type,
b.id detail_id,
b.loan loan,
0 payment
FROM person p
JOIN b ON p.id = b.personId
UNION ALL
SELECT p.id id,
p.name name,
1 type,
c.id detail_id,
0 loan,
c.payment payment
FROM person p
JOIN c ON p.id = c.personId
) q
ORDER BY id, detail_id, type
Then, I suppose you can use variables to generate the running totals. But Dr. Linoff is right (see his comment) that the dataset you've shown doesn't have enough information reliably to interleave loan and payment records. I've used ID fields to do this. The last ORDER BY really ought to mention posting_date or some other information instead of detail_id, if you have it elsewhere in your tables
try this:
select *, (sum(tb.loan) / sum(tc.payment)) as balance
from tblPerson tp
join tblB tb on tp.id = tb.personId
join tblC tc on tp.id = tc.personId
this is what comes to my head.