Updated question. I have following tables:
project table
projectid, project
stores table
storeid,storename,projectid
projectupdates table
trackerid,projectid, storeid,activityid,date
activity table
activityid, activity
projects may or may not have stores
stores may or may not have activity
for a store an activity can be repeated
With this, I want all the projects, all the
stores and updates for a store.
The activity if present should show only the
latest date for the activity.
I also want a summary row that counts each
activity where activity has a date. (if null
do not count).
I tried with following your code:
SELECT P.ProjectID, S.StoreID, MAX(CASE WHEN Activity = 'Meas Perm Received' THEN Date ELSE NULL END) AS MeasPermRcvd, MAX(CASE WHEN Activity = 'Not Visited' THEN Date ELSE NULL END) AS NotVisited, MAX(CASE WHEN Activity = 'Not Visited' THEN ReasonID ELSE '' END) AS NotVisitedReason, MAX(CASE WHEN Activity = 'Visited & Measured' THEN Date ELSE NULL END) AS Measured
FROM dbo.ProjectUpdates AS T INNER JOIN dbo.Project AS P ON T.ProjectID = P.ProjectID INNER JOIN dbo.ProjectStores AS S ON T.StoreID = S.StoreID INNER JOIN dbo.Activity AS A ON T.ActivityID = A.ActivityID
GROUP BY P.ProjectID, S.StoreID
Then I tried with this additional query to get all projects and stores:
SELECT dbo.Project.ProjectID, dbo.ProjectStores.StoreID,dbo.StoreTrackerBase.MeasPermRcvd, dbo.StoreTrackerBase.NotVisited, dbo.StoreTrackerBase.NotVisitedReason, dbo.StoreTrackerBase.Measured
FROM dbo.Project FULL OUTER JOIN dbo.ProjectStores ON dbo.Project.ProjectID = dbo.ProjectStores.ProjectID FULL OUTER JOIN dbo.StorTrackerBase ON dbo.ProjectStores.StoreID = dbo.StoreTrackerBase.StoreID AND dbo.ProjectStores.ProjectID = dbo.StoreTrackerBase.ProjectID
This is returning all projects and all stores with latest date for activity.
Please advise whether this is good way of coding and whether performance will be good.
Please note I dont have a summary row in this query ( dont know how to do!)
thanks. Krishna
You can use MAX(CASE WHEN Activity = 'ActivityType' THEN Date END) with Group by ProjectName,Store
SELECT
ProjectName,Store,
MAX(CASE WHEN Activity = 'Visited' THEN Date ELSE NULL END) AS Visited,
MAX(CASE WHEN Activity = 'NotVisited' THEN Date ELSE NULL END) AS NotVisited,
MAX(CASE WHEN Activity = 'Finished' THEN Date ELSE NULL END) AS Finished
FROM Table1 T
INNER JOIN Project P
ON T.ProjectID = P.ProjectID
INNER JOIN Store S
ON T.StoreID = S.StoreID
INNER JOIN Activity A
ON T.ActivityID = A.ActivityID
GROUP BY ProjectName,Store
Related
In my application the users can create campaigns for sending messages. When the campaign tries to send a message, one of the three things can happen:
The message is suppressed and not let through
The message can't reach the recipient and is considered failed
The message is successfully delivered
To keep track of this, I have the following table:
My problem is that when the application has processed a lot of messages (more than 10 million), the query I use for showing campaign statistics for the user slows down by a considerable margin (~ 15 seconds), even when there are only a few (~ 10) campaigns being displayed for the user.
Here is the query I'm using:
select `campaigns`.*, (select count(*) from `processed_messages`
where `campaigns`.`id` = `processed_messages`.`campaign_id` and `status` = 'sent') as `messages_sent`,
(select count(*) from `processed_messages` where `campaigns`.`id` = `processed_messages`.`campaign_id` and `status` = 'failed') as `messages_failed`,
(select count(*) from `processed_messages` where `campaigns`.`id` = `processed_messages`.`campaign_id` and `status` = 'supressed') as `messages_supressed`
from `campaigns` where `user_id` = 1 and `campaigns`.`deleted_at` is null order by `updated_at` desc;
So my question is: how can I make this query run faster? I believe there should be some way of not having to use sub-queries multiple times but I am not very experienced with MySQL syntax yet.
You should write this as a single join, using conditional aggregation:
SELECT
c.*,
COUNT(CASE WHEN pm.status = 'sent' THEN 1 END) AS messages_sent,
COUNT(CASE WHEN pm.status = 'failed' THEN 1 END) AS messages_failed,
COUNT(CASE WHEN pm.status = 'suppressed' THEN 1 END) AS messages_suppressed
FROM campaigns c
LEFT JOIN processed_messages pm
ON c.id = pm.campaign_id
WHERE
c.user_id = 1 AND
c.deleted_at IS NULL
GROUP BY
c.id
ORDER BY
c.updated_at DESC;
It should be noted that at first glance, doing SELECT c.* appears to be a violation of the GROUP BY rules which say that only columns which appear in the GROUP BY clause can be selected. However, assuming that campaigns.id is the primary key column, then there is nothing wrong with selecting all columns from this table, provided that we aggregate by the primary key.
Edit:
If the above answer does not run on your MySQL server version, with an error message complaining about only full group by, then use this version:
SELECT c1.*, c2.messages_sent, c2.messages_failed, c2.message_suppressed
FROM campaigns c1
INNER JOIN
(
SELECT
c.id
COUNT(CASE WHEN pm.status = 'sent' THEN 1 END) AS messages_sent,
COUNT(CASE WHEN pm.status = 'failed' THEN 1 END) AS messages_failed,
COUNT(CASE WHEN pm.status = 'suppressed' THEN 1 END) AS messages_suppressed
FROM campaigns c
LEFT JOIN processed_messages pm
ON c.id = pm.campaign_id
WHERE
c.user_id = 1 AND
c.deleted_at IS NULL
GROUP BY
c.id
) c2
ON c1.id = c2.id
ORDER BY
c2.updated_at DESC;
In below query (Mentors) are 13 which shows me 26, while (SchoolSupervisor) are 5 which shows me 10 which is wrong. it is because of the Evidence which having 2 evidance, because of 2 evidence the Mentors & SchoolSupervisor values shows me double.
please help me out.
Query:
select t.c_id,t.province,t.district,t.cohort,t.duration,t.venue,t.v_date,t.review_level, t.activity,
SUM(CASE WHEN pr.p_association = "Mentor" THEN 1 ELSE 0 END) as Mentor,
SUM(CASE WHEN pr.p_association = "School Supervisor" THEN 1 ELSE 0 END) as SchoolSupervisor,
(CASE WHEN count(file_id) > 0 THEN "Yes" ELSE "No" END) as evidence
FROM review_m t , review_attndnce ra
LEFT JOIN participant_registration AS pr ON pr.p_id = ra.p_id
LEFT JOIN review_files AS rf ON rf.training_id = ra.c_id
WHERE 1=1 AND t.c_id = ra.c_id
group by t.c_id, ra.c_id order by t.c_id desc
enter image description here
You may perform the aggregations in a separate subquery, and then join to it:
SELECT
t.c_id,
t.province,
t.district,
t.cohort,
t.duration,
t.venue,
t.v_date,
t.review_level,
t.activity,
pr.Mentor,
pr.SchoolSupervisor,
rf.evidence
FROM review_m t
INNER JOIN review_attndnce ra
ON t.c_id = ra.c_id
LEFT JOIN
(
SELECT
p_id,
COUNT(CASE WHEN p_association = 'Mentor' THEN 1 END) AS Mentor,
COUNT(CASE WHEN p_association = 'School Supervisor' THEN 1 END) AS SchoolSupervisor,
FROM participant_registration
GROUP BY p_id
) pr
ON pr.p_id = ra.p_id
LEFT JOIN
(
SELECT
training_id,
CASE WHEN COUNT(file_id) > 0 THEN 'Yes' ELSE 'No' END AS evidence
FROM review_files
GROUP BY training_id
) rf
ON rf.training_id = ra.c_id
ORDER BY
t.c_id DESC;
Note that this also fixes another problem your query had, which was that you were selecting many columns which did not appear in the GROUP BY clause. Under this refactor, there is nothing wrong with your current select, because the aggregation take place in a separate subquery.
try adding this to the WHERE part of your query
AND pr.p_id IS NOT NULL AND rf.training_id IS NOT NULL
You can add a group by pr.p_id to remove the duplicate records there. Since, the group by on pr is not present as of now, there might be multiple records of same p_id for same ra
group by t.c_id, ra.c_id, pr.p_id order by t.c_id desc
I have this code below. I am trying to merge the rows based on date.
SELECT
TICKETS.TICKETID,
RECEIPTS.DATENEW,
TAXCATEGORIES.NAME = 'GCT' as GCT,
TAXCATEGORIES.NAME = 'Tax 25%' as Tax25,
TAXLINES.AMOUNT,
SUM(TAXLINES.AMOUNT) AS TOTAL,
SUM(CASE WHEN taxcategories.NAME = 'GCT' THEN taxlines.AMOUNT ELSE 0 END) AS GCTTOTAL,
SUM(CASE WHEN taxcategories.NAME = 'Tax 25%' THEN taxlines.AMOUNT ELSE 0 END) AS TAX25TOTAL
FROM
RECEIPTS,
TAXLINES,
TAXES,
TAXCATEGORIES,
TICKETS,
PAYMENTS
WHERE
PAYMENTS.RECEIPT = RECEIPTS.ID
AND RECEIPTS.ID = TAXLINES.RECEIPT
AND RECEIPTS.ID = TICKETS.ID
AND TAXLINES.TAXID = TAXES.ID
AND TAXES.CATEGORY = TAXCATEGORIES.ID
AND DATENEW >= '2016-07-14 00:00:00' AND DATENEW <= '2016-07-14 23:00:00'
GROUP BY gct, Tax25, CAST(RECEIPTS.DATENEW AS DATE)
The Result of the query is attached in the screenshot below:
Now I need help to merge those rows that have the same date into one row. I am not sure where I am going wrong, I've tried a series of joins but I'm coming up blank.
It appears that the records with the same date actually differ in their values for the GCT and Tax25 columns. If you remove these columns from the GROUP BY clause, and instead aggregate them in the SELECT list, you would be left with a single record for the duplicate dates.
Note that in the query below I have replaced your implicit joins (using a comma-separated list of tables in the FROM clause) with explicit inner joins, with the join criteria in the ON clause. This is the standard way of writing queries now, and it makes them much easier to read. If INNER JOIN is too restrictive, then perhaps you intended to use LEFT JOIN instead.
SELECT TICKETS.TICKETID,
RECEIPTS.DATENEW,
MAX(TAXCATEGORIES.NAME) = 'GCT' as GCT, -- one value per date
MAX(TAXCATEGORIES.NAME) = 'Tax 25%' as Tax25, -- one value per date
TAXLINES.AMOUNT,
SUM(TAXLINES.AMOUNT) AS TOTAL,
SUM(CASE WHEN taxcategories.NAME = 'GCT' THEN taxlines.AMOUNT ELSE 0 END) AS GCTTOTAL,
SUM(CASE WHEN taxcategories.NAME = 'Tax 25%' THEN taxlines.AMOUNT ELSE 0 END) AS TAX25TOTAL
FROM RECEIPTS
INNER JOIN TAXLINES
ON RECEIPTS.ID = TAXLINES.RECEIPT
INNER JOIN TAXES
ON TAXLINES.TAXID = TAXES.ID
INNER JOIN TAXCATEGORIES
ON TAXES.CATEGORY = TAXCATEGORIES.ID
INNER JOIN TICKETS
ON RECEIPTS.ID = TICKETS.ID
INNER JOIN PAYMENTS
ON PAYMENTS.RECEIPT = RECEIPTS.ID
WHERE DATENEW >= '2016-07-14 00:00:00' AND
DATENEW <= '2016-07-14 23:00:00'
GROUP BY CAST(RECEIPTS.DATENEW AS DATE)
Hi The following query does not return all projects and all stores. It shows only a store which has at least one status date filled.
How can I get all the projects from projects table and all stores from stores table and also show the status date for the stores (wherever it is filled).
SELECT
ProjectName,Store,
MAX(CASE WHEN Activity = 'Visited' THEN Date ELSE NULL END) AS Visited,
MAX(CASE WHEN Activity = 'NotVisited' THEN Date ELSE NULL END) AS NotVisited,
MAX(CASE WHEN Activity = 'Finished' THEN Date ELSE NULL END) AS Finished
FROM Table1 T
INNER JOIN Project P
ON T.ProjectID = P.ProjectID
INNER JOIN Store S
ON T.StoreID = S.StoreID
INNER JOIN Activity A
ON T.ActivityID = A.ActivityID
GROUP BY ProjectName,Store
SELECT
BB.NAME BranchName,
VI.NAME Village,
COUNT(BAC.CBSACCOUNTNUMBER) 'No.Of Accounts',
SUM(BAC.CURRENTBALANCE) SumOfAmount,
SUM(CASE
WHEN transactiontype = 'C' THEN amount
ELSE 0
END) AS CreditTotal,
SUM(CASE
WHEN transactiontype = 'D' THEN amount
ELSE 0
END) AS DebitTotal,
SUM(CASE
WHEN transactiontype = 'C' THEN amount
WHEN transactiontype = 'D' THEN - 1 * amount
ELSE 0
END) AS CurrentBalance
FROM
CUSTOMER CU,
APPLICANT AP,
ADDRESS AD,
VILLAGE VI,
BANKBRANCH BB,
BANKACCOUNT BAC
LEFT OUTER JOIN
accounttransaction ACT ON BAC.CBSACCOUNTNUMBER = ACT.BANKACCOUNT_CBSACCOUNTNUMBER
AND ACT.TRANDATE <= '2013-03-21'
AND BAC.ACCOUNTOPENINGDATE < '2013-03-21'
AND ACT.BANKACCOUNT_CBSACCOUNTNUMBER IS NOT NULL
WHERE
CU.CODE = AP.CUSTOMER_CODE
AND BAC.ENTITY = 'CUSTOMER'
AND BAC.ENTITYCODE = CU.CODE
AND AD.ENTITY = 'APPLICANT'
AND AD.ENTITYCODE = AP.CODE
AND AD.VILLAGE_CODE = VI.CODE
AND AD.STATE_CODE = VI.STATE_CODE
AND AD.DISTRICT_CODE = VI.DISTRICT_CODE
AND AD.BLOCK_CODE = VI.BLOCK_CODE
AND AD.PANCHAYAT_CODE = VI.PANCHAYAT_CODE
AND CU.BANKBRANCH_CODE = BB.CODE
AND BAC.CBSACCOUNTNUMBER IS NOT NULL
AND ACT.TRANSACTIONTYPE IS NOT NULL
GROUP BY BB.NAME , VI.NAME;
Here is my information
I have two tables bankaccount and accountransactions table
If account is created it will go to bankaccount table and if any transaction is done so respective account number record in accounttrasactiosns table however I want to display the count of total account numbers respective to the branch which the account number existed in bankaccount and it is may or may not available in accounttransactions table.
I'm guessing that the problem you have is that you are not getting results for accounts that do not have data in your accounttransaction table, even though you are using a LEFT JOIN. If that is true, the reason is because your join condition includes AND ACT.BANKACCOUNT_CBSACCOUNTNUMBER IS NOT NULL, which defeats the LEFT JOIN. You also have two conditions in your WHERE clause that I bet should not be there.
You should learn to use explicit join syntax in your coding. Your code will be much clearer if you do that; it separates the join conditions from the WHERE clause, which does a very different thing. I took a stab at re-writing your query as an illustration:
SELECT
BB.NAME BranchName,
VI.NAME Village,
COUNT(BAC.CBSACCOUNTNUMBER) 'No.Of Accounts',
SUM(BAC.CURRENTBALANCE) SumOfAmount,
SUM(ACT.CurrentBalance) CurrentBalance,
SUM(ACT.DebitTotal) DebitTotal,
SUM(ACT.CreditTotal) CreditTotal
FROM CUSTOMER CU
JOIN APPLICANT AP
ON AP.CUSTOMER_CODE = CU.CODE
JOIN ADDRESS AD
ON AD.ENTITYCODE = AP.CODE
JOIN VILLAGE VI
ON VI.CODE = AD.VILLAGE_CODE
AND VI.STATE_CODE = AD.STATE_CODE
AND VI.DISTRICT_CODE = AD.DISTRICT_CODE
AND VI.BLOCK_CODE = AD.BLOCK_CODE
AND VI.PANCHAYAT_CODE = AD.PANCHAYAT_CODE
JOIN BANKBRANCH BB
ON BB.CODE = CU.BANKBRANCH_CODE
JOIN BANKACCOUNT BAC
ON BAC.ENTITYCODE = CU.CODE
LEFT OUTER JOIN (
SELECT BANKACCOUNT_CBSACCOUNTNUMBER,
SUM(CASE
WHEN transactiontype = 'C' THEN amount
ELSE 0
END) AS CreditTotal,
SUM(CASE
WHEN transactiontype = 'D' THEN amount
ELSE 0
END) AS DebitTotal,
SUM(CASE
WHEN transactiontype = 'C' THEN amount
WHEN transactiontype = 'D' THEN - 1 * amount
ELSE 0
END) AS CurrentBalance
FROM accounttransaction
WHERE TRANDATE <= '2013-03-21'
GROUP BY BANKACCOUNT_CBSACCOUNTNUMBER
) ACT
ON ACT.BANKACCOUNT_CBSACCOUNTNUMBER = BAC.CBSACCOUNTNUMBER
AND BAC.ACCOUNTOPENINGDATE < '2013-03-21'
WHERE BAC.ENTITY = 'CUSTOMER'
AND AD.ENTITY = 'APPLICANT'
GROUP BY BB.NAME , VI.NAME;
I removed this line from the LEFT JOIN condition
AND ACT.BANKACCOUNT_CBSACCOUNTNUMBER IS NOT NULL
And I removed these two lines from the WHERE clause
AND BAC.CBSACCOUNTNUMBER IS NOT NULL
AND ACT.TRANSACTIONTYPE IS NOT NULL
If that does not solve your problem, please revise your question to explain further.
UPDATE: Based on comments, the query is revised to calculate the debit, credit, and current balance by account using a derived table.
Also note the placement of the BAC.ACCOUNTOPENINGDATE < '2013-03-21' condition on left join. As written, this will return all accounts regardless of the opening date. If you want to only show accounts that were opened before that date, this condition should be moved to the WHERE clause.