Set status value with group by on different values with SQL - php

I have this SQL query
SELECT SUM(reach) AS reach, SUM(impressions) AS impressions, cpc, id_name,
SUM(clicks) AS clicks, SUM(amount_spent) AS amount, pagename, status
FROM mbk_ad_data
WHERE id_campaign_shortname = 'name'
AND adset_name NOT LIKE '%MSN%'
AND date_from = '2016-02-02'
AND date_to = '2016-02-09'
GROUP BY id_name
That will group and output this:
reach impressions cpc id_name clicks amount pagename status
4099 4529 6.34875 name 29 246.11 Name paused
This works almost as intended. I have two different rows with "pagename" Name and the summed up values are correct, but row 1 has status "active" and row 2 has status "paused". What I want is to have a status "active" in the output, if one of the rows has a status "active", so my output will be:
reach impressions cpc id_name clicks amount pagename status
4099 4529 6.34875 name 29 246.11 Name active
How can do this in the query?

Apply MIN on status:
SELECT SUM(reach) AS reach, SUM(impressions) AS impressions, cpc, id_name,
SUM(clicks) AS clicks, SUM(amount_spent) AS amount, pagename,
MIN(status) AS status
FROM mbk_ad_data
WHERE id_campaign_shortname = 'name'
AND adset_name NOT LIKE '%MSN%'
AND date_from = '2016-02-02'
AND date_to = '2016-02-09'
GROUP BY id_name
This way status = 'active' will take precedence over status = 'paused'. This will work as long as the group contains just these two values for status field.

You should get into the habit of including all non-aggregated columns in the GROUP BY.
Then, the rest can be done using aggregation:
SELECT SUM(reach) AS reach, SUM(impressions) AS impressions, cpc, id_name,
SUM(clicks) AS clicks, SUM(amount_spent) AS amount, pagename,
(CASE WHEN MAX(status = 'active') > 0 THEN 'active'
ELSE MAX(status)
END) as status
FROM mbk_ad_data
WHERE id_campaign_shortname = 'name' AND adset_name NOT LIKE '%MSN%' AND
date_from = '2016-02-02' AND date_to = '2016-02-09'
GROUP BY id_name, cpc, id_name, pagename;
Although there might be a very slight performance hit for including all the columns, including all the columns more than makes up for that problem by (1) being standard SQL and (2) by preventing unintentional errors.

Related

Sum columns on different tables and multiply by value of a column on another table

I need to compute employees' monthly salaries based on meetings attended, deductions and bonuses given;
Employees have different pay per meeting based on their job position.
The solution is:
salary = (Pay_per_minute * meetings_attended) + bonuses - deductions ;
I have four tables:
Jobs: Id, title, pay_per_meeting
Employees: Id, Name, job_id
Bonuses: Id, amount, employee_id, date
Deductions: Id, amount, employee_id, date
Meetings: Id, employee_id, date
SELECT
COUNT(meetings.employee_id) as meetings_attended,
COUNT(deductions.amount) as debt,
COUNT(bonuses.amount) bonus,
(SELECT jobs.pay_per_attendance from jobs where jobs.id = (select job_id from employees where id=meetings.employee_id)) as pay,
((meetings_attended * pay) + bonus - debt) as salary
FROM meetings
JOIN deductions ON deductions.employee_id = meetings.employee_id
JOIN bonuses ON bonuses.employee_id = meetings.employee_id
WHERE meetings.employee_id = 1
GROUP BY MONTH(meetings.date), MONTH(deductions.date), MONTH(bonuses.date)
The above query returns many incorrect values whenever i remove the salary line but gives error of unknown column pay, meetings_attended, debt and bonus, am sure something is wrong with the grouping but i can't just see it.
You can't refer to column aliases in the same select list as they're defined, you need to refer to the underlying column. And a subquery can't access an aggregate calculated in the main query. You need to repeat the aggregate expression, or move everything into a subquery and do the calculation with it in an outer query.
Also, all your COUNT() expressions are going to return the same thing, since they're just counting rows (I assume none of the values can be NULL). You probably want COUNT(DISTINCT <column>) to get different counts, and you need to use a column that's unique, so they should be the primary key column, e.g. COUNT(DISTINCT deductions.id).
Another problem is that when you try to sum and count values when you have multiple joins, you end up with a result that's too high, because rows get duplicated in the cross product of all the tables. See Join tables with SUM issue in MYSQL. The solution is to calculate the sums from each table in subqueries.
SELECT m.month, m.meetings_attended, d.debt, b.bonus,
m.meetings_attended * j.pay_per_meeting + b.amount - d.amount AS salary
FROM (
SELECT MONTH(date) AS month, COUNT(*) AS meetings_attended
FROM meetings
WHERE employee_id = 1
GROUP BY month) AS m
JOIN (
SELECT MONTH(date) AS month, COUNT(*) AS bonus, SUM(amount) AS amount
FROM bonuses
WHERE employee_id = 1
GROUP BY month) AS b ON m.month = b.month
JOIN (
SELECT MONTH(date) AS month, COUNT(*) AS debt, SUM(amount) AS amount
FROM deductions
WHERE employee_id = 1
GROUP BY month) AS d ON m.month = d.month
CROSS JOIN employees AS e
JOIN jobs AS j ON j.id = e.job_id
WHERE e.employee_id = 1

How to write SQL statement for this case?

I have 3 tables as below:
tbl_campaign_name
tbl_backer
payment_cellcard
I want to select all the campaign name from tbl_campaign_name, but with the combination of tbl_backer and payment_cellcard.
How should the combination look like?
Well, there are fields as amount in tbl_backer, but it has a status field also, which i consider 1 as approved, 0 as unapproved. payment_cellcard has no status, which means it is always approved.
The problem:
I want to select the campaign name from tbl_campaign_name to list them down in one list, with the amount of money that have been approved from tbl_backer and sum with payment_cellcard which is always approved.
below is my query, which it sum up the amount even the status from tbl_backer is not approved yet.
SELECT
tbl_campaign_name.camp_id AS camp_id,
tbl_campaign_name.is_featured AS is_featured,
SUM(ifnull(payment_cellcard.payment_cellcard_amount, 0))
AS cellcard,
SUM( ifnull(tbl_backer.total_amount, 0))
AS backed_amount,
(SUM( ifnull(payment_cellcard.payment_cellcard_amount, 0)) +
SUM( ifnull(tbl_backer.total_amount, 0)))
AS total_donation
FROM ((tbl_campaign_name
LEFT JOIN tbl_backer
ON ((tbl_campaign_name.camp_id = tbl_backer.camp_id)))
LEFT JOIN payment_cellcard
ON ((tbl_campaign_name.camp_id = payment_cellcard.camp_id)))
WHERE (tbl_campaign_name.is_featured = 1)
GROUP BY tbl_campaign_name.camp_id
ORDER BY (SUM( ifnull(payment_cellcard.payment_cellcard_amount, 0)) + SUM(ifnull(tos_backer.total_amount, 0))) DESC,
tbl_campaign_name.camp_id
The result:
I got all the project listed as I want, but it sum up even the
tbl_backer.backer_status = 0
I hope this is clear explanation of what I want.
Thanks,

SQL request with ORDER, DISTINCT and COUNT

My table has 4 columns: id, name, ip, timestamp
I'm trying to get results as following: "Show me for each "name" the count of rows you have with distinct "ip", and non-distinct ip (the total)"
I'm using this table to store all clicks users did on some links and I want to show a top click: for each "name" the amount of users who did click, and the total amount of clicks for this "name".
Is that even possible in one SQL request ?
Do it like this:
select name
, count(*) total_clicks
, count(distinct ip) distinct_ppl
from table_name
group by name
order by name /* or by count(*) desc or count(distinct ip) desc */
You can get the top 10 clickers like this:
SELECT count(ip),* FROM table GROUP BY ip ORDER BY count(ip) LIMIT 10;

Select COUNT of column and GROUP BY

Here I am with a problem I can't solve myself.
My problem is:
I need to SELECT the count of a column, but I also need to GROUP BY on that same column. What I've tried so far is not returning as I expected.
Here is what I tried:
'SELECT COUNT(user_id) AS total_donors
FROM ' . DONATION_SECURITY_TABLE .
" WHERE payment_status = 'Completed'
GROUP BY user_id"
This is how my table looks like:
id user_id payment_status
1 20 Completed
2 33 Completed
3 44 Completed
4 20 Pending
5 33 Pending
6 44 Completed
7 20 Completed
As you see, a single user_id can be Pending or Completed more than once, but I want my query to return 3 (based on the table example above).
So, I want my query to COUNT GROUPED user_ids if payment status is completed.
Ideas?
You can use the DISTINCT keyword to select the unique User ID's like so
"SELECT COUNT(DISTINCT user_id) AS total_donors
FROM " . DONATION_SECURITY_TABLE .
" WHERE payment_status = 'Completed'
sounds like a distinct count is what you need. throw away the group by and try this:
SELECT
COUNT(DISTINCT user_id) AS total_donors
FROM
mytable
WHERE
payment_status = 'Completed'
For a result of 3, replace your Count(user_id) with Count(distinct user_id) and remove the group by.
That gives you the count of unique user_ids with a payment status of completed.
Use this:
SELECT COUNT(DISTINCT `user_id`) as `Total` FROM `Donation_Table`
WHERE `payment_status` = 'Completed'
since you are only interested on the count of user_ID, you don't need to use GROUP BY clause
SELECT COUNT(DISTINCT user_ID)
FROM tableName
WHERE payment_status = 'completed'
when you try to add GROUP BY clause. the result is very different from what you are expecting.

PHP: SELECT ing 2 tables?

I have a activities page and a statusmessages page for each user.
In activities it contains what the users have done, such as being friends with someone, commented on pictures and so.
users_activities
id | uID | msg | date
In users_statusmessages, I got the statusmessages, the user creates.
users_statuses
id | uID | message | date
uID is the userĀ“s id.
Now i would like to select both of them and while{} them in one. Where they get sorted by date desc ( as the date in both tables is UNIX timestamp).
So something like WHERE uID = '$userID' ORDER BY date DESC
So example of how i wish it to look:
User: Today is a good day (message) (date: 1284915827) (users_statuses)
User have added as Jack as friend (msg) (date: 1284915811) (users_activities)
User: I have a hard day today (message) (date: 1284915801) (users_statuses)
User have commented on Jacks picture (msg) (date: 1284915776) (users_activities)
How should i do this the right way?
You need to use the UNION operator:
SELECT ua.msg,
ua.date,
'activity' AS is_table
FROM USERS_ACTIVITIES ua
WHERE ua.uid = '{$userID}'
UNION ALL
SELECT us.message,
us.date,
'status'
FROM USERS_STATUSES us
WHERE us.uid = '{$userID}'
ORDER BY `date`
UNION
UNION removes duplicates. UNION ALL does not remove duplicates, and is faster for it.
But the data types at each position in the SELECT must match. In the query provided, you can see that the date column is referenced in the second position. You'd get an error if the column order were reversed between the first and second query.
The ORDER BY is applied to the result of the UNION'd query in standard SQL. In MySQL, that includes the LIMIT clause. But MySQL also supports putting brackets around the UNION'd queries so you can use ORDER BY & LIMIT before the queries are UNION'd.
You're going to want to use a union
http://dev.mysql.com/doc/refman/5.0/en/union.html
This is untested...
(SELECT uID, msg as message, date from users_activities)
UNION
(SELECT uId, message, date from users_statuses) order by date desc limit 20
There are a lot more examples on that page
Something like this would do
SELECT act.*,status.* FROM users_activities act, users_statuses status WHERE act.id = status.id AND status.id = '$UID' ORDER BY status.date,act.date DESC LIMIT 30
Spaced out for visual purposes:
SELECT
act.*,status.*
FROM
users_activities act,
users_statuses status
WHERE
act.id = status.id
AND
status.id = '$UID'
ORDER BY
status.date,act.date DESC
LIMIT
30

Categories