What's wrong with this group by query in MySQL? - mysql

My DB structure:
Each "wp_custombill" have many "wp_customuser"
I want to query all the custombill, each custombill have sum of its customuser. My query is
select cb.ID, cb.Description, cb.amount, SUM(cu.amount)
from wp_customuser as cu
inner join wp_custombill as cb
on cu.email = cb.Email
group by cb.ID, cb.Description, cb.amount
But the result seem like sum(cu.Amount) summary all of CustomUser's records.

My guess is your wp_custombill ids (1,2,3) have same email
But without data sample is hard to test.
Otherwise your query looks ok.

In GROUP BY clause we have to write the field, over which we decide the aggregation is done. For your situation your field is customer not the bill. For each customer we have to sum the bill amount. So the query would look like-
select cu.payentId, cb.ID, cb.Description, cb.amount, SUM(cu.amount)
from wp_customuser as cu
inner join wp_custombill as cb
on cu.email = cb.Email
group by cu.payentId
In this query we find the all bill associated with single customer by join on user table on email. You can use cu.email or cb.email in group by also.

you don't need to group with all columns. if you group them with description or amount field, then you may get duplicate records.
select cb.ID, cb.Description, cb.amount, SUM(cu.amount)
from wp_customuser as cu
inner join wp_custombill as cb
on cu.email = cb.Email
group by cb.email

Related

MYSQL query to get project details and last MAX() action details from log

How can I write a MYSQL query to get project details and the entire last row of the activity log? I want a list of all the projects, with the data from each project's most recent row from the action log, all of it ordered by the most recent action log date DESC. Sorry, I know that this is a common query and the answer must be very easy. But I can't find the solution. I searched with every possible word combination. I found examples that need only one field such as MAX(id) from the joined table. I found solutions with COALESCE but can't seem to make them work. My problem is that I need many fields from the 'parent' table row PL_PROJECTS as well as many fields from the joined table PL_LOG row, not to mention people's names from the same table joined twice.
Everything I try either gives me all the rows of the PL_LOG, repeating rows from PL_PROJECTS. Or, I get just one row from PL_LOG for just one project if I put a LIMIT in the sub query. Here's my query that doesn't work:
SELECT
PJ.pj_id, PJ.pj_title, PJ.pj_location, PJ.pj_desc, PJ.pj_request, PJ.pj_date_start, PP1.pp_name AS supervisor_name, PP2.pp_name AS customer_name, ST.st_desc, logDate, logDesc
FROM PL_PROJECTS PJ
INNER JOIN PL_PEOPLE PP1 ON PJ.pj_spst_member = PP1.pp_id
INNER JOIN PL_PEOPLE PP2 ON PJ.pj_pp_id = PP2.pp_id
INNER JOIN PL_STATUS ST ON PJ.pj_status = ST.st_id
LEFT OUTER JOIN (
SELECT MAX(lg_pj_id) MaxLogID, lg_date AS logDate, lg_desc AS logDesc, lg_pj_id
FROM PL_LOG PL
ORDER BY lg_id DESC
)
LR ON LR.lg_pj_id = PJ.pj_id
GROUP BY PJ.pj_id
ORDER BY logDate DESC
LIMIT 9999999
I think you problem is, that your subselect only generates one row as you are using max() while you need one row per project (lg_pj_id i think).
You only need to rewrite the subselect to generate one row per project with the informations from the recent activity. Do you have an activity_ID in your action log? Because it looks like
lg_pj_id is the project_ID. The meaning of lg_desc is also unknown (or is that the action_log_id ?). Try to group by project_ID in you subselect and depending on your needs either select the max values from the corresponding rows or select the row with the maximum values per group (project_ID)
Thanks for the suggestion of GROUP BY to get one row per project. I tried changing the sub-query like so:
SELECT MAX(lg_id) AS MaxLogID, lg_desc, lg_pj_id
FROM PL_LOG PL
GROUP BY lg_pj_id
Now, I get one row from the log, but it gives me the max id, but not the lg_desc from the same row! If I try the sub-query by itself:
SELECT lg_id, lg_pj_id, lg_date, lg_desc
FROM `PL_LOG`
WHERE lg_pj_id = 33
ORDER BY lg_date DESC
I get these rows. You can see the max row, 68 has a description "30 minute skype call."
68,33,2018-06-10 00:00:00","30 minute skype call."
61,33,"2018-06-02 00:00:00","Sent email to try to elicit a response."
52,33,"2018-05-10 00:00:00","sent follow up email"
47,33,"2018-03-26 00:00:00","sent initial email"
46,33,"2018-03-26 00:00:00","sent initial email"
But when I try to get just that row, using GROUP BY, it gives me the max lg_id, but the first lg_desc. I need the data all from the max(lg_id) row:
SELECT MAX(lg_id) AS MaxLogID, lg_pj_id, lg_date, lg_desc
FROM PL_LOG
WHERE lg_pj_id = 33
GROUP BY lg_pj_id
ORDER BY MaxLogID DESC
Returns:
68, 33, "2018-03-26 00:00:00", "sent initial email"
Try this as mentioned in my comment:
SELECT
PJ.pj_id, PJ.pj_title, PJ.pj_location, PJ.pj_desc, PJ.pj_request,
PJ.pj_date_start, PP1.pp_name AS supervisor_name, PP2.pp_name AS
customer_name, ST.st_desc, logDate, logDesc
FROM PL_PROJECTS PJ
INNER JOIN PL_PEOPLE PP1 ON PJ.pj_spst_member = PP1.pp_id
INNER JOIN PL_PEOPLE PP2 ON PJ.pj_pp_id = PP2.pp_id
INNER JOIN PL_STATUS ST ON PJ.pj_status = ST.st_id
LEFT JOIN (SELECT lg_id, lg_date AS logDate, lg_desc AS logDesc, lg_pj_id
FROM PL_LOG AS PL
WHERE PL.lg_id=(SELECT MAX(lg_id) FROM PL_LOG AS PL_2
WHERE PL_LOG.lg_pj_id = PL_2.lg_pj_id )
LR ON LR.lg_pj_id = PJ.pj_id
GROUP BY PJ.pj_id
ORDER BY logDate DESC
LIMIT 9999999

SQL Aggregate with join giving incorrect results

In a bid to learn SQL i've added some dummy data into a few tables that i generated in Excel. I've got a table for customer, order headers and order lines.
Im trying to check that the customers balance, order header total and line totals all match.
But when I run this query I get the incorrect output for the orderheader, i believe it to be becuase its doing the SUM for the amount of times the orderlines table is referenced.
Can anyone tell me the correct way i should be doing it?
SELECT
cus.cus_id,
cus.cus_name,
cus.cus_balance,
SUM(orderheader.orderheader_currentsell) AS orderHeader_total,
SUM(orderlines.orderlines_currentsell) AS orderLines_total
FROM
cus
JOIN
orderheader ON orderheader.orderHeader_customer = cus.cus_id
JOIN
orderlines ON orderlines.orderlines_orderid = orderheader.orderHeader_id
GROUP BY cus.cus_name
output ( the highlighted column should be the same as the other values.)
You have multiple rows for the header. To solve this, aggregate before doing the join. In your case, just aggregating the order lines should be sufficient:
SELECT c.cus_id, c.cus_name, c.cus_balance,
SUM(oh.orderheader_currentsell) AS orderHeader_total,
SUM(ol.orderLines_total) AS orderLines_total
FROM cus c JOIN
orderheader oh
ON oh.orderHeader_customer = c.cus_id JOIN
(SELECT ol.orderlines_orderid, SUM((ol.orderlines_currentsell) as orderLines_total
FROM orderlines ol
GROUP BY ol.orderlines_orderid
) ol
ON ol.orderlines_orderid = oh.orderHeader_id
GROUP BY cus.cus_name;
Because you have different levels of grouping, it's not that trivial, and you need subselects.
You can calculate the total per customer as a subselect in the field list. In the code below I've done that just for the orders, but you could do the same for the order lines which are still solved by the grouping.
SELECT
cus.cus_id,
cus.cus_name,
cus.cus_balance,
( SELECT
SUM(orderheader_currentsell)
FROM
orderheader
WHERE
orderheader.orderHeader_customer = cus.cus_id) AS orderHeader_total,
SUM(orderlines.orderlines_currentsell) AS orderLines_total
FROM
cus
JOIN
orderlines ON orderlines.orderlines_orderid = orderheader.orderHeader_id
GROUP BY cus.cus_name
This is at first glance, but I am noticing you have:
cus.cus_id,
cus.cus_name,
cus.cus_balance,
as the non-aggregate columns. But in your Group-By you only have:
GROUP BY cus.cus_name
Group By should include all of the non-aggregate columns. This may be why you're not getting the expected results. That would be changed to:
GROUP BY cus.cus_id,
cus.cus_name,
cus.cus_balance

COUNT evaluate to zero if no matching records

Take the following:
SELECT
Count(a.record_id) AS newrecruits
,a.studyrecord_id
FROM
visits AS a
INNER JOIN
(
SELECT
record_id
, MAX(modtime) AS latest
FROM
visits
GROUP BY
record_id
) AS b
ON (a.record_id = b.record_id) AND (a.modtime = b.latest)
WHERE (((a.visit_type_id)=1))
GROUP BY a.studyrecord_id;
I want to amend the COUNT part to display a zero if there are no records since I assume COUNT will evaluate to Null.
I have tried the following but still get no results:
IIF(ISNULL(COUNT(a.record_id)),0,COUNT(a.record_id)) AS newrecruits
Is this an issue because the join is on record_id? I tried changing the INNER to LEFT but also received no results.
Q
How do I get the above to evaluate to zero if there are no records matching the criteria?
Edit:
To give a little detail to the reasoning.
The studies table contains a field called 'original_recruits' based on activity before use of the database.
The visits tables tracks new_recruits (Count of records for each study).
I combine these in another query (original_recruits + new_recruits)- If there have been no new recruits I still need to display the original_recruits so if there are no records I need it to evalulate to zero instead of null so the final sum still works.
It seems like you want to count records by StudyRecords.
If you need a count of zero when you have no records, you need to join to a table named StudyRecords.
Did you have one? Else this is a nonsense to ask for rows when you don't have rows!
Let's suppose the StudyRecords exists, then the query should look like something like this :
SELECT
Count(a.record_id) AS newrecruits -- a.record_id will be null if there is zero count for a studyrecord, else will contain the id
sr.Id
FROM
visits AS a
INNER JOIN
(
SELECT
record_id
, MAX(modtime) AS latest
FROM
visits
GROUP BY
record_id
) AS b
ON (a.record_id = b.record_id) AND (a.modtime = b.latest)
LEFT OUTER JOIN studyrecord sr
ON sr.Id = a.studyrecord_id
WHERE a.visit_type_id = 1
GROUP BY sr.Id
I solved the problem by amending the final query where I display the result of combining the original and new recruits to include the IIF there.
SELECT
a.*
, IIF(IsNull([totalrecruits]),consents,totalrecruits)/a.target AS prog
, IIf(IsNull([totalrecruits]),consents,totalrecruits) AS trecruits
FROM
q_latest_studies AS a
LEFT JOIN q_totalrecruitment AS b
ON a.studyrecord_id=b.studyrecord_id
;

Mysql: Unable to fetch value from a select query inside a query

I have the following query. If I run it I get this error message.
Query-
SELECT account_name,ABC,date FROM entries
LEFT JOIN accounts ON accounts.id = entries.accounts_id
LEFT JOIN voucher ON voucher.id = entries.trans_id
WHERE trans_id IN ( SELECT trans_id, amount AS ABC FROM entries
WHERE accounts_id='$accounts_id' AND side='C')
AND accounts_id!='$accounts_id' AND side='D'
AND voucher.date between '$dateragne1' AND '$dateragne2'
I think the problem is with the value ABC. It is unable to fetch the value from the second query.
Could you please tell me how to fix this query?
Thanks in Advance :)
Try this:
SELECT account_name, _inner.ABC, date
FROM
(
SELECT amount AS ABC FROM entries
WHERE accounts_id='$accounts_id' AND side='C'
) AS _inner, entries
LEFT JOIN accounts ON accounts.id = entries.accounts_id
LEFT JOIN voucher ON voucher.id = entries.trans_id
WHERE trans_id IN
(
SELECT trans_id FROM entries WHERE accounts_id='$accounts_id' AND side='C'
)
AND accounts_id!='$accounts_id' AND side='D'
AND voucher.date between '$dateragne1' AND '$dateragne2'`
Notes:
Using subquery like this doesn't allow you to request a fields from it.
Also, IN statement use data from only only column, not two.

SELECT with JOIN limited

I have the follow query:
SELECT pro.*
FROM tb_AutProposta pro, tb_AutParcelamento par
WHERE pro.id = par.id
But, want to limit each tb_AutParcelamento to 1. Tried "Subselect", but without success.
The table pro is an contract and par is the parcels of this contract. For each contract, is generated n parcels, and, for each parcel is generated a due date, and I need to know the last due date for each contract.
Any idea?
I just made up some field names because we don't know your exact table structure.
This query works under the assumptions:
you want the latest ("highest") parcel due date for each related contract
you have fields for due_date and pro_id in your parcel table. (tb_AutParcelamento.pro_id being a foreign key to the tb_AutProposta.id)
i made up pro_id because i assume the condition pro.id = par.id is wrong, when the id fields in both tables are auto increment values and the primary key in each table.
SELECT pro.*, MAX(par.due_date) as latest_due_date
FROM tb_AutProposta pro
LEFT JOIN tb_AutParcelamento par
ON pro.id = par.pro_id
GROUP BY pro.id
Without really knowing your data schema, and what you're trying to do, you could try to limit the number of par records returned.
SELECT pro.*, FIRST(par.Id) as FirstParcel
FROM tb_AutProposta pro, tb_AutParcelamento par
WHERE pro.id = par.id
GROUP BY pro.*
This is a question involving the retrieval of the group-wise maximum/minimum of a set of records. The method I like to use is as follows:
SELECT
a.*,
b.due_date
FROM
tb_AutProposta a
INNER JOIN
tb_AutParcelamento b ON a.id = b.id
INNER JOIN
(
SELECT id, MAX(due_date) AS last_due_date
FROM tb_AutParcelamento
GROUP BY id
) c ON b.id = c.id AND b.due_date = c.last_due_date
Replace due_date with the actual name of the date field in tb_AutParcelamento.