There's a query where I want to get the:
Username of the user attached to the current opportunity record
Sales Stage associated with each opportunity record
Dollar amount associated with opportunity record
I want to:
Take the current IF STATEMENT result, and collapse it
Current Query:
$sql = "SELECT u.user_name as USER,
if(o.sales_stage='Prospecting', o.amount, '') as PROSPECTING,
if(o.sales_stage='Needs Analysis', o.amount, '') as NEEDS_ANALYSIS,
if(o.sales_stage='Closed Won', o.amount, '') as CLOSED_WON
FROM opportunities o,
users u
WHERE o.assigned_user_id = u.id
GROUP BY u.user_name ";
Current Result:
USER PROSPECTING NEEDS_ANALYSIS CLOSED_WON
---------------------------------------------
chris 10000 0 0
chris 0 15000 0
chris 0 0 10000
sara 5000 0 0
sara 0 0 10000
What I'd like to do is collapse the results where I only get 1 user, and their respective amounts per SalesStage
USER PROSPECTING NEEDS_ANALYSIS CLOSED_WON
---------------------------------------------
chris 10000 15000 10000
sara 5000 0 10000
The "collapsing" is generally referred to as "pivoting", because you're converting row into columnar data. Use:
SELECT u.user_name as USER,
MAX(CASE WHEN o.sales_stage = 'Prospecting' THEN o.amount END) AS PROSPECTING,
MAX(CASE WHEN o.sales_stage = 'Needs Analysis' THEN o.amount END) AS NEEDS_ANALYSIS,
MAX(CASE WHEN o.sales_stage = 'Closed Won' THEN o.amount END) AS CLOSED_WON
FROM OPPORTUNITIES o
JOIN USERS u ON u.id = o.assigned_user_id
GROUP BY u.user_name
you need to add aggregating functions to your $ amounts... ie:
SELECT
u.user_name as USER,
if(o.sales_stage='Prospecting', SUM(o.amount), '') as PROSPECTING,
if(o.sales_stage='Needs Analysis', SUM(o.amount), '') as NEEDS_ANALYSIS,
if(o.sales_stage='Closed Won', SUM(o.amount), '') as CLOSED_WON
FROM
opportunities o,
users u
WHERE
o.assigned_user_id = u.id
GROUP BY
u.user_name
EDIT
Now that I think about it, you'll probably still get separate rows because the sales stages may differ among records for the same user. Have you considered using a routine to do the calculations for each sales stage to collapse your results down to one row per user?
You could wrap the current query in another query, like this:
select subquery.user,
sum(subquery.propspecting),
sum(subquery.needs_analysis),
sum(subquery.closed_won)
from (*...your query goes here*) subquery
group by subquery.user
Related
I have one table where I would like to count the amount of names grouped by school, count whether school has one or two genders as well as count the specific occurence of a particular value in 3 different columns. I am able to do the counts in two different tables but I want to join them to make 1 table
FName
School
Gender
Events
Events2
Events3
Ann
Marymount
F
HJ
LJ
TJ
Peter
Marymount
M
100
200
400
Drew
St Hughs
M
100
200
Davis
St Hughs
M
200
Kat
Campion
F
400
Molly
Campion
F
400
Mike
Marymount
M
800
Fran
Campion
M
100
200
These are the 2 separate queries I use successfully and create two different result sets
$sql = "SELECT COUNT(FName) as cnt, COUNT(DISTINCT(Gender)) as gnd, school, COUNT(*) FROM entries WHERE school <> 'Unattached' GROUP BY school";
$sql = "SELECT COUNT(Events + Events2 + Events3) as events, school, COUNT(*) FROM entries WHERE (school <> 'Unattached') AND (Events = '100') OR (Events2 = '100') OR (Events3 = '100') GROUP BY school";
$result = $conn->query($sql);
I have tried using a union but it only gives me results for second query and the count of names is off.
$sql = "
SELECT COUNT(FName) as cnt, COUNT(Events + Events2 + Events3) as events, COUNT(DISTINCT(Gender)) as gnd, school
FROM entries WHERE (school <> 'Unattached')
UNION
SELECT COUNT(FName) as cnt, COUNT(Events + Events2 + Events3) as events, COUNT(DISTINCT(Gender)) as gnd, school
FROM entries WHERE (school <> 'Unattached') AND (Events = '100') OR (Events2 = '100') OR (Events3 = '100')
GROUP BY school";
$result = $conn->query($sql);
The left join gave me no results because I didn't know how to fit the 2 WHERE clauses.
How can I join the two queries to get one table with the following result
School
# of Participants
# Genders
# 100
Marymount
3
2
1
St Hughs
2
1
1
Campion
3
2
1
Use SUM() to add up the number of rows where a condition is true. This works because a boolean value is 1 when it's true, 0 when it's false, so SUM() gets this count.
SELECT school,
COUNT(*) AS num_participants,
COUNT(DISTINCT gender) AS num_genders,
SUM(events = '100' OR events2 = '100' OR events3 = '100') AS num_100
FROM entries
WHERE school != 'Unattached'
GROUP BY school
► Context : I work in a museum (for real), people come everyday, they buy tickets for themselves (humans) and sometimes they also buy tickets for drinks and foods (objects). There are events, tickets have the same names per event but the prices are different.
► The problem : I have to create a report with 2 results : total sales (visitors + food + drinks) and how many people came (visitors only) for a specific event. Next is an image of the 3 tables in the database, how they relate and some sample data :
Table TICKETS relates to SALES_MAIN through EVENT_ID column.
Table SALES_MAIN relates to SALES_DETAIL through ID→MAIN_ID columns.
Table SALES_DETAIL have a column TICKET_NAME but it's not unique in table TICKETS.
► The question : How to get both results, total sales and human count, for event 555 in one "select" ? I tried next 2 "select" but when I combine them with another INNER JOIN I get cartesian results :
Get detail sales for event 555 :
SELECT sales_detail.* FROM sales_main
INNER JOIN sales_detail ON sales_detail.main_id = sales_main.id
WHERE sales_main.event_id = '555'
Get tickets for event 555 :
SELECT * FROM tickets WHERE tickets.event_id = '555'
Use:
SELECT
SUM(CASE WHEN sd.ticket_name IN ('adult', 'child') THEN sd.quantity
ELSE 0 END) AS total_visitors,
SUM(sd.quantity * t.price) AS total_sales
FROM sales_main sm
JOIN sales_detail sd
ON sd.main_id = sm.id
JOIN ticket t
ON t.event_id = sm.event_id
AND t.ticket_name = sd.ticket_name
WHERE sm.event_id = '555';
Conditional aggregation could also be based on type:
SUM(CASE WHEN t.ticket_type ='human' THEN sd.quantity ELSE 0 END)
The following mySQL query gets data from 2 tables, alerts_data and alerts_list. The first table has the data of an alert, and the second has the description of the alert. So in the alerts_data there are multiple rows with the same alerts_data_id that is the same with the alerts_id of alerts_list.
What i want to achieve, is to display something like this
alert number 51, 5 clicked , 2 closed
alert number 57, 13 clicked, 3 closed, 8 waiting
using mySQL or PHP (i do not know if i can get this through plain mySQL)
So for now with my knowledge I can not display the data of alert 51 in one row, but because of the different alerts_data_status i have to show 3 rows for each.
How can I do it as above?
SELECT COUNT( alerts_data_id ) AS total, alerts_data_id, alerts_data_status, alerts_list.alerts_title
FROM alerts_data
JOIN alerts_list ON
alerts_data.alerts_data_id = alerts_list.alerts_id
GROUP BY alerts_data_id, alerts_data_status
//output
total - alerts_data_id - alerts_data_status - alerts_title
5 - 51 - clicked - alert number 51
2 - 52 - closed - alert number 51
13 - 57 - clicked - alert number 57
3 - 57 - waiting - alert number 57
8 - 57 waiting - alert number 57
Note: the alerts number are just examples, it can be any number
// alert_data
id - alerts_data_id - alerts_data_status
// alerts_list
alerts_id - alerts_name - alerts_text
Here's a sqlfiddle: http://sqlfiddle.com/#!9/c70c2/1
This may be an application for GROUP_CONCAT().
You first want a summary of your alerts by alerts_data_id and alerts_data_status. This is a little complex, because your sqlfiddle has a whole bunch of empty alerts_data_status strings. Here, I'm replacing those empty strings with `?'. (http://sqlfiddle.com/#!9/c70c2/23/0)
SELECT COUNT(*) AS alerts_count,
alerts_data_id,
CASE WHEN LENGTH(alerts_data_status) = 0 THEN '?'
ELSE alerts_data_status END AS alerts_data_status
FROM alerts_data
GROUP BY alerts_data_id, alerts_data_status
You then want to roll that up inside another query
SELECT SUM(a.alerts_count) total,
a.alerts_data_id, b. alerts_name,
GROUP_CONCAT( CONCAT(a.alerts_count, ': ', a.alerts_data_status)
ORDER BY a.alerts_data_status
SEPARATOR "; " ) detail
FROM (
SELECT COUNT(*) AS alerts_count,
alerts_data_id,
CASE WHEN LENGTH(alerts_data_status) = 0 THEN '?'
ELSE alerts_data_status END AS alerts_data_status
FROM alerts_data
GROUP BY alerts_data_id, alerts_data_status
) a
JOIN alerts_list b ON a.alerts_data_id = b.alerts_id
GROUP BY a.alerts_data_id, b.alerts_name
This will give you one row for each distinct alerts_data_id. Each alert is identified by its count, its id, and its name. (http://sqlfiddle.com/#!9/c70c2/26/0)
Then the row will contain a semicolon-separated list of the counts of the different alert status.
If i understand you well i think here is what you need;
SELECT
COUNT( alerts_data.id ) AS total,
ad.id,
(SELECT count(*) from alert_list al where al.alerts_id=ad.id and alerts_data_status='clicked') as clicked,
(SELECT count(*) from alert_list al where al.alerts_id=ad.id and alerts_data_status='closed') as closed,
(SELECT count(*) from alert_list al where al.alerts_id=ad.id and alerts_data_status='waiting') as waiting,
FROM alerts_data ad
GROUP BY ad.id
Was checking other answers and your SQLFIDDLE and thought this might be a nicer approach:
SELECT alerts_list.alerts_title,
SUM(CASE WHEN alerts_data_status = 'clicked' THEN 1 ELSE 0 END) AS clicked,
SUM(CASE WHEN alerts_data_status = 'closed' THEN 1 ELSE 0 END) AS closed,
SUM(CASE WHEN alerts_data_status = 'waiting' THEN 1 ELSE 0 END) AS waiting
FROM alerts_data
JOIN alerts_list ON alerts_data.alerts_data_id = alerts_list.alerts_id
GROUP BY alerts_list.alerts_title
My query below will get the last 3 active threads from my blog's commnets,
SELECT ID, Approved, RecipientID, RecipientScreenname, RecipientEmail
FROM
(
SELECT
root_strings.str_id as ID,
root_strings.str_approved as Approved,
root_strings.mem_id as RecipientID,
root_members_cfm.mem_screenname as RecipientScreenname,
root_members_cfm.mem_firstname as RecipientFirstname,
root_members_cfm.mem_email as RecipientEmail
FROM root_strings
LEFT JOIN root_members_cfm
ON root_members_cfm.mem_id = root_strings.mem_id
WHERE root_strings.parent_id = '1'
AND root_strings.mem_id IS NOT NULL
UNION ALL
SELECT
root_strings.str_id as ID,
root_strings.str_approved as Approved,
root_strings.mem_id as RecipientID,
root_users.usr_screenname as RecipientScreenname,
root_users.usr_firstname as RecipientFirstname,
root_users.usr_email as RecipientEmail
FROM root_strings
LEFT JOIN root_users
ON root_users.usr_id = root_strings.usr_id
WHERE root_strings.parent_id = '1'
AND root_strings.usr_id IS NOT NULL
) SQ
ORDER BY ID DESC
LIMIT 0,3
It returns a result like this,
ID Approved RecipientID RecipientScreenname RecipientEmail
14 1 3 x x#yahoo.co.uk
13 n/a NULL y y#yahoo.co.uk
13 n/a NULL y y#yahoo.co.uk
Then I will an email to each of them.
foreach($items_thread as $item_thread)
{
$sentmail = mail($item_thread['RecipientEmail'],$email_subject,$email_content,$email_headers);
}
But the logic is not correct when you look closer as I will send y twice of the email!
y should just get one email. How can I fix it - should I fix the sql query or the php code?
The problem is that NULL!=NULL (nor is NULL=NULL), so DISTINCT does not consider the 2nd and 3rd results to be the same. If you change your query so that RecipientID shows as 0 (e.g COALESCE(RecipientID,0) AS RID) the problem will go away.
I have a solution for this now which is using array_unique, here it is Remove duplicate items from an array
I am creating a bulletin board application. Each bulletin can be liked or disliked by users of the site.To keep track of the likes and dislikes I have created the following database table
id user_id bulletin_id like_dislike
1 1 1 1
2 1 2 0
3 3 1 1
4 2 1 0
In the like_dislike column 1 means 'Like It', 0 means 'Don't like it'
I know how to ask.
- How many times was bulletin 1 liked (2)
- How many times was bulletin 1 disliked (1)
But How do I do a query to ask those two questions at the same time? That is, how many times was bulletin 1 liked and disliked
liked disliked
2 1
I have tried the query
SELECT count(like_dislike) AS likes, count(like_dislike) AS dislikes FROM bulletins_ld
where bulletins_id = 1 AND likes = 1 AND dislikes = 0
but all I get is two twice which is not surprising.
The only solution I can think of is having a separate like and dislike column
You can do this with an aggregate query, using opposing conditions on the single like_dislike column (I am assuming below that a '1' in that column means 'liked').
SELECT bulletin_id,
SUM(CASE WHEN like_dislike = 1 THEN 1 ELSE 0 END) AS likes,
SUM(CASE WHEN like_dislike = 0 THEN 1 ELSE 0 END) AS dislikes
FROM bulletins_ld
GROUP BY bulletin_id
Update: As per the discussion in the comments below, the like/dislike column could be normalized into its own table, like so (example deliberately silly...):
CREATE TABLE how_user_feels(
feeling_id INT,
feeling_desc VARCHAR(20)
)
INSERT INTO how_user_feels(feeling_id, feeling_desc) VALUES
(0, 'Undecided'),
(1, 'Likes It'),
(2, 'Could Do Without It')
The Likes_Dislikes column in the Bulletin table is then replaced by the foreign key feeling_id, with a default to 0. Let's say that you then enter a record in this table when a user first views a bulletin, making them "Undecided" by default, and update that record when they vote on the bulletin. You could query the results like so:
SELECT bulletin_id,
SUM(CASE WHEN feelings_id = 1 THEN 1 ELSE 0 END) AS likes,
SUM(CASE WHEN feelings_id = 2 THEN 1 ELSE 0 END) AS dislikes,
SUM(CASE WHEN feelings_id = 0 THEN 1 ELSE 0 END) AS doesnt_seem_to_care
FROM bulletins_ld b
INNER JOIN how_user_feels h ON b.feeling_id = h.feeling_id
GROUP BY bulletin_id
Keep in mind, this is just one approach, and may not be useful in your case. But if you ever decided to change or expand the model by which a user expresses their feelings for a bulletin, say to a five-star rating system, you could do that without changing the database schema - just alter the records in the how_user_feels table, and the associated queries.
For the record, there is another way to obtain the same result set, whether it is faster or slower depends on the platform. This uses a scalar subquery instead of an aggregate on the outer query. The idea is, this is supposed to be easier if you think in terms of sets. Or in terms of Dimension-Facts.
First we have to straighten out your names. Let's call your "table" bulletin_like and the main bulletin table bulletin (bulletin_id is a very silly name for either of them, that is more of a column name). And just call the boolean column like (if it is 1, like is true; if it is 0, like is false; that's what boolean means). Use the singular form for names. SELECT name AS bulletin,
(SELECT COUNT(like)
FROM bulletin_like bl
WHERE bl.bulletin_id = b.bulletin_id
AND like = 1
) AS like,
(SELECT COUNT(like)
FROM bulletin_like bl
WHERE bl.bulletin_id = b.bulletin_id
AND like = 0
) AS dislike
FROM bulletin b
You asked for the Normalisation tag,. That bulletin_like "table" is not Normalised. Get rid of theIdiot column, it serves no purpose other than a redundant column and an additional index. The PK is (bulletin_id, user_id).
Unless you want users to post multiple likes and dislikes per poster per bulletin.
This query worked for me:
SELECT bulletin_id,
sum(like_dislike) AS likes,
sum((1-like_dislike)) AS dislikes
FROM bulletins_ld
GROUP BY (bulletin_id);
This assumes that likedislike = 1 means "LIKE IT" and likedislike = 0 means "DOES NOT LIKE IT".
ID LIKES DISLIKES
1 2 1
2 0 1