I have 3 tables, student, family and fee. Some families have more than one kid. Fees are recorded in fee table. Families makes many payments through the year.
I want to group the number of kids by family and get the total amount paid by each family.
My Qyery:
SELECT student.Family_ID, family.Family_Name, count(*) as kids_numb, sum (Amount) as Paid_Amount
FROM student, family, fee
WHERE student.Family_ID = family.Family_ID
AND fee.Family_ID = family.Family_ID
AND student.Status ='1'
GROUP BY student.Family_ID;
I need to sort Something like:
Family Name | # of kids | Fees | Paid | Balance
----------------------------------------------------
Lebon | 1 | 425 | 200 | 225
Lavoix | 2 | 700 | 150 | 550
Napper | 1 | 425 | 0 | 425
Major | 3 | 900 | 300 | 600
UPDATED based on your comments.
Try it this way
SELECT m.family_id, m.family_name, s.kids_numb, COALESCE(f.paid_amount, 0) paid_amount
FROM family m LEFT JOIN
(
SELECT family_id, COUNT(*) kids_numb
FROM student
WHERE status = 1
GROUP BY family_id
) s
ON m.family_id = s.family_id LEFT JOIN
(
SELECT family_id, SUM(amount) paid_amount
FROM fee
GROUP BY family_id
) f
ON m.family_id = f.family_id
WHERE s.family_id IS NOT NULL
Sample output:
| FAMILY_ID | FAMILY_NAME | KIDS_NUMB | PAID_AMOUNT |
|-----------|-------------|-----------|-------------|
| 1 | Lebon | 1 | 200 |
| 2 | Lavoix | 2 | 150 |
| 3 | Napper | 1 | 0 |
| 4 | Major | 3 | 300 |
Here is SQLFiddle demo
UPDATE2: to add a grand total
SELECT family_id, family_name, SUM(kids_numb) kids_numb, SUM(paid_amount) paid_amount
FROM
(
SELECT m.family_id, m.family_name, s.kids_numb, COALESCE(f.paid_amount, 0) paid_amount
FROM family m LEFT JOIN
(
SELECT family_id, COUNT(*) kids_numb
FROM student
WHERE status = 1
GROUP BY family_id
) s
ON m.family_id = s.family_id LEFT JOIN
(
SELECT family_id, SUM(amount) paid_amount
FROM fee
GROUP BY family_id
) f
ON m.family_id = f.family_id
WHERE s.family_id IS NOT NULL
) q
GROUP BY family_id, family_name WITH ROLLUP
HAVING (family_id IS NOT NULL AND
family_name IS NOT NULL)
OR (family_id IS NULL AND
family_name IS NULL)
Sample output:
| FAMILY_ID | FAMILY_NAME | KIDS_NUMB | PAID_AMOUNT |
|-----------|-------------|-----------|-------------|
| 1 | Lebon | 1 | 200 |
| 2 | Lavoix | 2 | 150 |
| 3 | Napper | 1 | 0 |
| 4 | Major | 3 | 300 |
| (null) | (null) | 7 | 650 |
MySQL GROUP BY non-standard extension will allow you to change this whole part
GROUP BY family_id, family_name WITH ROLLUP
HAVING (family_id IS NOT NULL AND
family_name IS NOT NULL)
OR (family_id IS NULL AND
family_name IS NULL)
with just
GROUP BY family_id WITH ROLLUP
but then instead of NULL in family_name column for a total row you'll have a value of last family name.
Sample output:
| FAMILY_ID | FAMILY_NAME | KIDS_NUMB | PAID_AMOUNT |
|-----------|-------------|-----------|-------------|
| 1 | Lebon | 1 | 200 |
| 2 | Lavoix | 2 | 150 |
| 3 | Napper | 1 | 0 |
| 4 | Major | 3 | 300 |
| (null) | Major | 7 | 650 |
Here is SQLFiddle demo
Related
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.
I have a accounts table like this
+-----------+--------------+
| fld_id| name |
+-----------+--------------+
| 1 | Bank1|
| 2 | Bank2|
| 4 | Bank3|
+-----------+--------------+
Revenue Income Table Like this
+-----------+--------------+---------------+-------------+
| fld_id | fld_type | fld_account id| fld_amount |
+-----------+--------------+---------------+-------------+
| 1 | Salry| 1 | 400 |
| 2 | Rent | 2 | 500 |
| 4 | Others | 1 | 1000 |
+-----------+--------------+---------------+-------------+
Payment Table Like This
+-----------+--------------+---------------+-------------+
| fld_id | fld_type | fld_account id| fld_amount |
+-----------+--------------+---------------+-------------+
| 1 | Food | 2 | 200 |
| 2 | Entertain | 2 | 300 |
| 4 | Transport | 1 | 400 |
+-----------+--------------+---------------+-------------+
I want a final balance table for accounts with sum of income, expence and balance like This Table --
+-----------+--------------+---------------+-------------+
| account | Income | Expence | Balance |
+-----------+--------------+---------------+-------------+
| Bank1 | 1400 | 400 | 1000 |
| Bank2 | 500 | 500 | 0 |
| Bank3 | 0 | 0 | 0 |
+-----------+--------------+---------------+-------------+
So far I write this query and getting income and expense but did't find any way to calculate balance, my query and result is --query
SELECT fld_account as account, Income, Expense
from tbl_accounts
LEFT JOIN (SELECT fld_account_id, SUM(fld_amount) as Income FROM tbl_revenue tr GROUP BY tr.fld_account_id) tc on fld_id=tc.fld_account_id
left JOIN (SELECT fld_account_id, SUM(fld_amount) as Expense FROM tbl_payment tp GROUP BY tp.fld_account_id) td on fld_id=td.fld_account_id
and the result is like below --
+-----------+--------------+---------------+
| account | Income | Expense |
+-----------+--------------+---------------+
| Bank1 | 1400 | 400 |
| Bank2 | 500 | 500 |
| Bank3 | Null | Null |
+-----------+--------------+---------------+
How can I calculate balance form payment and revenue table and join it with my account table? Any help is very appreciated.
Just use coalesce():
SELECT fld_account as account, COALESCE(Income, 0) as Income,
COALESCE(Expense, 0) as Expense,
( COALESCE(Income, 0) - COALESCE(Expense, 0) ) as balance
FROM tbl_accounts LEFT JOIN
(SELECT fld_account_id, SUM(fld_amount) as Income
FROM tbl_revenue tr
GROUP BY tr.fld_account_id
) tc
ON fld_id = tc.fld_account_id LEFT JOIN
(SELECT fld_account_id, SUM(fld_amount) as Expense
FROM tbl_payment tp
GROUP BY tp.fld_account_id
) td
ON fld_id = td.fld_account_id;
COALESCE() is the ANSI-standard function that returns the first non-NULL argument.
I am losing my mind over this problem, research has not helped. Is this an anti-pattern and hard because of that, or is it just me...
The IDEA here is that we have a single table for every material we use, we have multiple storage shelf's in which that material is stored on. Every stocklocation has individual quantities and minimum limits.
What I want to achieve is a single SQL-query that outputs this:
+-------+------+---------------------+----------+-----------------+
| SL_Id | M_Id | TimeCreated | Quantity | MinimumQuantity |
+-------+------+---------------------+----------+-----------------+
| 1 | 2 | 2017-11-02 13:04:18 | 10 | 5 |
| 1 | 3 | NULL | NULL | 15 |
| 1 | 4 | 2017-11-02 15:56:56 | 7 | NULL |
+-------+------+---------------------+----------+-----------------+
This is where I am at the moment....
+-------+------+---------------------+----------+-----------------+
| SL_Id | M_Id | TimeCreated | Quantity | MinimumQuantity |
+-------+------+---------------------+----------+-----------------+
| 1 | 2 | 2017-11-02 13:04:18 | 10 | NULL |
| 1 | 3 | NULL | NULL | 15 |
| 1 | 4 | 2017-11-02 15:56:56 | 7 | NULL |
+-------+------+---------------------+----------+-----------------+
Here are the tables:
Material
+----+-----------+
| Id | OrderCode |
+----+-----------+
| 2 | asdf |
| 3 | 75424 |
| 4 | 45567 |
+----+-----------+
StockLocation
+----+-------+
| Id | Label |
+----+-------+
| 1 | asdf |
+----+-------+
Inventory
+----+------------------+-------------+---------------------+----------+
| Id | StockLocation_Id | Material_Id | TimeCreated | Quantity |
+----+------------------+-------------+---------------------+----------+
| 1 | 1 | 2 | 2017-11-02 13:04:18 | 10 |
| 2 | 1 | 4 | 2017-11-02 15:23:26 | 9 |
| 3 | 1 | 4 | 2017-11-02 15:56:56 | 7 |
+----+------------------+-------------+---------------------+----------+
StockLocationMaterialLimit
+----+------------------+-------------+-----------------+
| Id | StockLocation_Id | Material_Id | MinimumQuantity |
+----+------------------+-------------+-----------------+
| 1 | 1 | 2 | 5 |
| 2 | 1 | 3 | 15 |
+----+------------------+-------------+-----------------+
This is the monster behind failure,
SELECT
SL_Id,
M_Id,
TimeCreated,
Quantity,
MinimumQuantity
FROM (
SELECT
SL.Id AS SL_Id,
M.Id AS M_Id,
I.TimeCreated AS TimeCreated,
I.Quantity AS Quantity,
NULL AS MinimumQuantity
FROM StockLocation SL, Material M
JOIN Inventory I on I.Id = (
SELECT Id FROM Inventory I1 WHERE I1.StockLocation_Id=SL.Id AND I1.Material_Id=M.Id ORDER BY I1.TimeCreated DESC LIMIT 1
)
UNION
SELECT
SL.Id AS SL_Id,
M.Id AS M_Id,
NULL AS TimeCreated,
NULL AS Quantity,
SLML.MinimumQuantity AS MinimumQuantity
FROM StockLocation SL, Material M
JOIN StockLocationMaterialLimit SLML on SLML.Id = (
SELECT Id FROM StockLocationMaterialLimit SLML1 WHERE SLML1.StockLocation_Id=SL.Id AND SLML1.Material_Id=M.Id LIMIT 1
)
) tst GROUP BY SL_Id,M_Id
It turned out to be as easy as adding MAX() on those fields which produced NULL values.
SELECT
M_Id,
SL_Id,
TimeCreated,
MAX(Quantity) AS Quantity,
MAX(MinimumQuantity) AS MinimumQuantity
FROM (
SELECT
M.Id AS M_Id,
SL.Id AS SL_Id,
I.TimeCreated AS TimeCreated,
I.Quantity AS Quantity,
NULL AS MinimumQuantity
FROM StockLocation SL, Material M
INNER JOIN Inventory I on I.Id = (
SELECT Id FROM Inventory I1 WHERE I1.StockLocation_Id=SL.Id AND I1.Material_Id=M.Id ORDER BY I1.TimeCreated DESC LIMIT 1
)
UNION
SELECT
M.Id AS M_Id,
SL.Id AS SL_Id,
NULL AS TimeCreated,
NULL AS Quantity,
SLML.MinimumQuantity AS MinimumQuantity
FROM StockLocation SL, Material M
INNER JOIN StockLocationMaterialLimit SLML on SLML.Id = (
SELECT Id FROM StockLocationMaterialLimit SLML1 WHERE SLML1.StockLocation_Id=SL.Id AND SLML1.Material_Id=M.Id LIMIT 1
)
) tst GROUP BY SL_Id, M_Id;
I have table it store hierarchy data in MySQL this table store stable relation but if each user less than 1000 buy removed and user User a lower level replace this is my code and work fine, after GROUP BY it contain all ancestor of descendant with compare then COUNT(*) AS level count level each user. This I have SQL code to compress data According to minimum buy for each user
+-------------+---------------+-------------+
| ancestor_id | descendant_id | path_length |
+-------------+---------------+-------------+
| 1 | 1 | 0 |
| 1 | 2 | 1 |
| 1 | 3 | 1 |
| 1 | 4 | 2 |
| 1 | 5 | 3 |
| 1 | 6 | 4 |
| 2 | 2 | 0 |
| 2 | 4 | 1 |
| 2 | 5 | 2 |
| 2 | 6 | 3 |
| 3 | 3 | 0 |
| 4 | 4 | 0 |
| 4 | 5 | 1 |
| 4 | 6 | 2 |
| 5 | 5 | 0 |
| 5 | 6 | 1 |
| 6 | 6 | 0 |
+-------------+---------------+-------------+
This is table buy
+--------+--------+
| userid | amount |
+--------+--------+
| 2 | 2000 |
| 4 | 6000 |
| 6 | 7000 |
| 1 | 7000 |
SQL code
SELECT a.*
FROM
( SELECT userid
FROM webineh_user_buys
GROUP BY userid
HAVING SUM(amount) >= 1000
) AS buys_d
JOIN
webineh_prefix_nodes_paths AS a
ON a.descendant_id = buys_d.userid
JOIN
(
SELECT userid
FROM webineh_user_buys
GROUP BY userid
HAVING SUM(amount) >= 1000
) AS buys_a on (a.ancestor_id = buys_a.userid )
JOIN
( SELECT descendant_id
, MAX(path_length) path_length
FROM webineh_prefix_nodes_paths
where a.ancestor_id = ancestor_id
GROUP
BY descendant_id
) b
ON b.descendant_id = a.descendant_id
AND b.path_length = a.path_length
GROUP BY a.descendant_id, a.ancestor_id
I need get max path_length where ancestor_id have At least 1000 amount buy but have error in where in subquery where a.ancestor_id = ancestor_id error code
1054 - Unknown column 'a.ancestor_id' in 'where clause'
I add SQLFidle demo.
You could use this query:
select m.userid as descendant,
p.ancestor_id,
p.path_length
from (
select b1.userid,
min(case when b2.amount >= 1000
then p.path_length
end) as path_length
from (select userid, sum(amount) amount
from webineh_user_buys
group by userid
having sum(amount) >= 1000
) as b1
left join webineh_prefix_nodes_paths p
on p.descendant_id = b1.userid
and p.path_length > 0
left join (select userid, sum(amount) amount
from webineh_user_buys
group by userid) as b2
on p.ancestor_id = b2.userid
group by b1.userid
) as m
left join webineh_prefix_nodes_paths p
on p.descendant_id = m.userid
and p.path_length = m.path_length
order by m.userid
Output for sample data in the question:
| userid | ancestor_id | path_length |
|--------|-------------|-------------|
| 1 | (null) | (null) |
| 2 | 1 | 1 |
| 4 | 2 | 1 |
| 6 | 4 | 2 |
SQL fiddle
I need help generating SQL for MySQL database.
I have three tables:
Organisations
Members
Payments
Organisations table:
+------------+---------+--------+
| id | name |website |
+------------+---------+--------+
| 1 | AAA | a.com |
|-------------------------------+
| 2 | BBB | b.com |
+------------+---------+--------+
Members table:
+------------+-------------------+--------+-----------------+-----------+
| id | organisation_id |name | Payment_confirm | join_date |
+------------+-------------------+--------+-----------------+-----------+
| 1 | 1 | james | 1 | 2013-8-02 |
|-----------------------------------------+-----------------+-----------+
| 2 | 1 | Jimmy | 0 | 2013-6-25 |
+------------+-------------------+--------+-----------------+-----------+
| 3 | 2 | Manny | 1 | 2013-07-02|
|-----------------------------------------+-----------------+-----------+
| 4 | 1 | Kim | 1 | 2013-09-02|
+------------+-------------------+--------+-----------------+-----------+
Payments table:
+------------+-------------------+--------+-----------------+----------------+
| id | member_id |amount | transaction_id | transferred_at |
+------------+-------------------+--------+-----------------+----------------+
| 1 | 1 | 100 | T1001 | 2013-8-03 |
|-----------------------------------------+-----------------+--------------- +
| 2 | 2 | 0 | null | Null |
+------------+-------------------+--------+-----------------+----------------+
| 3 | 3 | 200 | T1002 | Null |
|-----------------------------------------+-----------------+----------------+
| 4 | 4 | 50 | T1005 | 2013-09-05 |
+------------+-------------------+--------+-----------------+----------------+
How can I select the following?
Expecting the following output:
+------------+-------------------+--------+-----------------+---------------+--------------+
| Org name | Revenue |untransferred amount | Total members | last 30 days |
+------------+-------------------+--------------------------+---------------+--------------+
| AAA | 150 | 0 | 3 | 2 |
|-----------------------------------------------------------+---------------+--------------+
| BBB | 200 | 200 | 1 | 0 |
+------------+-------------------+--------------------------+---------------+--------------+
Org name = organisation name
Revenue = Total amount received
untransferred amount = transferred_at is null (payments table)
Total members = total members joined till today
last 30 days = total members joined last 30 days
You need to join your tables, group the results and select the desired logic:
SELECT org.name,
SUM(pmt.amount) AS revenue,
SUM(IF(pmt.transferred_at IS NULL, pmt.amount, 0)) AS untransferred
FROM Organisations org
JOIN Members mem ON mem.organisation_id = org.id
JOIN Payments pmt ON pmt.member_id = mem.id
GROUP BY org.id
See it on sqlfiddle.
select o.name,
sum(amount) as Revenue,
sum(if(transferred_at is null, amount, 0)) as untransfered_ammt,
sum(if(join_date>=curdate() - interval 30 day, 1, 0)) as last_30_d
from organisations o
inner join members m on o.id=m.organisation_id
inner join payments p on p.member_id=m.member_id
group by 1