SQL Select statement from multiple tables while adding values - mysql

I'm having a bit of trouble figuring out a good statement to write. I am able to achieve what I want when I query a specific 'Company' but I wanting to get the values for all of the companies in the database.
Basically I have 3 tables: Users, Companies, Plans_ExchangeMailbox. What I need to do is query how many plans are in use for each company. The plans are assigned at the user level in the users table.
Here is my table layouts:
USERS
DisplayName
CompanyCode (This is the ID from the CompanyCode in the Companies table)
MailboxPlan (This is the ID from the Plans_ExchangeMailbox Table)
Companies
CompanyName
CompanyCode
Plans_ExchangeMailbox
MailboxPlanName
MailboxPlanID
Here is the format I am looking to generate:
CompanyName, MailboxPlanName, Count (this is the number of MailboxPlanID for a company)
I was able to get this working but the problem is it can only do one company at a time and it doesn't get the CompanyName:
SELECT
Plans_ExchangeMailbox.MailboxPlanName,
SUM(CASE WHEN Users.MailboxPlan = Plans_ExchangeMailbox.MailboxPlanId THEN 1 ELSE 0 END) AS PlanCount
FROM
Plans_ExchangeMailbox, Users
WHERE
Users.CompanyCode='CC0'
GROUP BY
Plans_ExchangeMailbox.MailboxPlanName
The Final Format How it Should Be:
Headers: CompanyName, PlanName, Count
Values:
Microsoft, Bronze Plan, 5
Microsoft, Gold Plan, 20
Dell, Bronze Plan, 3
Dell, Silver Plan, 80
etc.....

Try this:
SELECT
C.CompanyName,
E.MailboxPlanName,
COUNT(1) Cnt
FROM Companies C
JOIN Users U
ON C.CompanyCode = U.CompanyCode
JOIN Plans_ExchangeMailbox E
ON U.MailboxPlan = E.MailboxPlanID
GROUP BY
C.CompanyCode,
C.CompanyName,
E.MailboxPlanID,
E.MailboxPlanName
Grouped by C.CompanyCode and E.MailboxPlanID in case if there are different companies or MailboxPlan with same name. If no,you can remove them from GROUP BY clause.

Related

Crosstab Query on multiple data points

I have a table that tracks employee quality assessment data. It includes the employee name, 5 yes/no fields tracking important items and the date the user did each task as column headings. Each employee gets 10 records a month so it includes a lot of data about how well our employees are doing at those 5 tasks.
I would like a report that shows me the monthly averages of these 5 yes/no fields: Appeal, NRP, Churn, Protocol, and Resub. I want those to be the Row Headers. I want the column headers to be sequential Months and the Averages to be the values. I can do this with a crosstab query for a single item such as avg:Appeal as the value and the user as the row header. How can I construct my query to use all 5 yes/no fields? They hoped for result would look like:
Table image showing how I want it to look
Comments on the Correct Answer:
June7 came up with a great answer! I changed the True to False in the DataUNION query because I wanted the Accuracy percentage and the true indicates an error on the employee evaluation. I also added in a few fields I didn't mention before. Thank you very much for helping a scrub out June7! Reading through what you wrote inspired me to start taking an SQL course on Lynda. I know its basic but you have to start somewhere and I'm getting to the point where access's builtin functions aren't doing it for me. Hopefully with the next question I'll be able to address the concerns of the commentators below that were upset that I didn't have code for myself that I had tried first.
June7's revised Code
Consider:
Query1: DataUNION
SELECT ID AS SourceID, Emp, Year([TaskDate]) AS Yr, Format([TaskDate], "mmm") AS Mo, "Appeal" AS Trend
FROM Data
WHERE Appeal=True
UNION SELECT ID, Emp, Year([TaskDate]), Format([TaskDate], "mmm"), "NRP"
FROM Data WHERE NRP = True
UNION SELECT ID, Emp, Year([TaskDate]), Format([TaskDate], "mmm"), "Churn"
FROM Data WHERE Churn = True
UNION SELECT ID, Emp, Year([TaskDate]), Format([TaskDate], "mmm"), "Protocol"
FROM Data WHERE Protocol = True
UNION SELECT ID, Emp, Year([TaskDate]), Format([TaskDate], "mmm"), "Resub"
FROM Data WHERE Resub = True;
Query2: DataCOUNT
SELECT DataUNION.Yr, DataUNION.Mo, DataUNION.Trend,
Count(DataUNION.Emp) AS CountOfEmp, Q.CntYrMo, Count([Emp])/[CntYrMo]*100 AS Pct
FROM (SELECT Year([TaskDate]) AS Yr, Format([TaskDate],"mmm") AS Mo, Count(Data.ID) AS CntYrMo
FROM Data
GROUP BY Year([TaskDate]), Format([TaskDate],"mmm")) AS Q
INNER JOIN DataUNION ON (Q.Yr = DataUNION.Yr) AND (Q.Mo = DataUNION.Mo)
GROUP BY DataUNION.Yr, DataUNION.Mo, DataUNION.Trend, Q.CntYrMo;
Query3:
TRANSFORM First(DataCount.Pct) AS FirstOfPct
SELECT DataCount.Yr, DataCount.Trend
FROM DataCount
GROUP BY DataCount.Yr, DataCount.Trend
PIVOT DataCount.Mo In ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");

Concatenate references of duplicate values in MySQL

I have a table (chapter) that contains 5 columns for officers in an organization: ID (key), president, vice_president, secretary, treasurer. For each office there is the value of a reference number to an individual.
For some IDs, the same value is listed for more than one of the 4 offices. You can see a basic example of my data structure below:
ID president vice_president secretary treasurer
105 1051456 1051456 1051466 1051460
106 1060923 1060937 1060944 1060944
108 1081030 1081027 1081032 1081017
110 1100498 1100491 1100485 1100485
I have also posted the same at http://sqlfiddle.com/#!9/57df1
My goal is to identify when a value is in more than one field and to SELECT that value as well as a concatenated list of all of the column titles in which it is found. For example from the supplied sample dataset, I would ideally like to return the following:
member offices
1051456 president, vice_president
1060944 secretary, treasurer
1100485 secretary, treasurer
I have found a few other examples that are similar, but nothing seems work towards what I am looking to do. I'm a novice but can piece things together from examples fairly well. I was also thinking that there might be an easier way by joining with the information_schema database as that is how I have pulled column titles in the past. It doesn't seem that this should as difficult as it is, and hopefully I am missing an easy and obvious solution. My full dataset is rather large and I would prefer to avoid any intensive sub-queries for the sake of performance. My SQL format is MySQL 5.5.
Any help or guidance would be greatly appreciated!
One method uses union all to unpivot the data and then re-aggregates:
select member, group_concat(office)
from ((select id, president as member, 'president' as office from t) union all
(select id, vice_president, 'vice_president' as office from t) union all
(select id, secretary, 'secretary' as office from t) union all
(select id, treasurer, 'treasurer' as office from t)
) t
group by member
having count(distinct office) > 1;
If you want to control the order of the values, then add a priority:
select member, group_concat(office order by priority) as offices
from ((select id, president as member, 'president' as office, 1 as priority from t) union all
(select id, vice_president, 'vice_president' as office, 2 from t) union all
(select id, secretary, 'secretary' as office, 3 from t) union all
(select id, treasurer, 'treasurer' as office, 4 from t)
) t
group by member
having count(distinct office) > 1;

MySQL alternative to subquery/join

I am looking for an efficient alternative to subqueries/joins for this query. Let's say I a table that stores information about companies with the following columns:
name: the name of the company
state: the state the company is located
in
revenue: the annual revenue of the company
employees: how many
employees this company has
active_business: wether or not the company
is in business (1 = yes, 0 = no)
Let's say that from this table, I want to find out how many companies in each state meet the requirement for some minimum amount of revenue, and also how many companies meet the requirement for some minimum number of employees. This can be expressed as the following subquery (can also be written as a a join):
SELECT state,
(
SELECT count(*)
FROM records AS a
WHERE a.state = records.state
AND a.revenue > 1000000
) AS companies_with_min_revenue,
(
SELECT count(*)
FROM records AS a
WHERE a.state = records.state
AND a.employees > 10
) AS companies_with_min_employees
FROM records
WHERE active_business = 1
GROUP BY state
My question is this. Can I do this without the subqueries or joins? Since the query is already iterating over each row (there's no indexes), is there some way I can add a condition that if the row meets the minimum revenue requirements and is in the same state, it will increment some sort of counter for the query (similar to map/reduce)?
I think CASE and SUM will solve it:
SELECT state
, SUM(CASE WHEN R.revenue > 1000000 THEN 1 ELSE 0 END) AS companies_with_min_revenue
, SUM(CASE WHEN R.employees > 10 THEN 1 ELSE 0 END) AS companies_with_min_employees
FROM records R
WHERE R.active_business = 1
GROUP BY R.state
As you can see, we will have a value of 1 per record with a revenue of greater than 1000000 (else 0), then we'll take the sum. The same goes with the other column.
Thanks to this StackOverflow question. You'll find this when you search "sql conditional count" in google.

MySQL dynamic order

In general, all I need to do is to order a MySQL table.
But, it has to be a "smart" order and I would like to hear your opinions.
There is a table of customers
id, name, email, phone, country, language, registration_time
There is another table which holds the skills of sales managers as numeric values -
sales_manager_id, skill_type, skill_name, skill_value
7, language , English , 5
Which means that manager number 7 speaks English on level 5.
Every sales manager can have multiple skills.
Now, I want to order the customers table by country, language and registration_time (in this exact order) for a specific sales manager in such a way that the top rows will be from a country in which this sales manager has highest skills, after this by language in which he has the highest skills and after this by registration time.
Do you have any suggestions? The biggest problem is that this query should be simple and readable as much as possible because there would be modifications in the future and I don't want to deal with enormous queries.
I assume I understand your problem. If not please correct me, else here is my solution.
I did a few changes to your tabels in order to test the query and you can test my solution like I did here: SQL Fiddle
(1) You need to JOIN your customers table two times with the skills table - first for the country skill and second for the language skill of a sales manager:
select *
from customers
join skills as country_skills on country_skills.skill_name = customers.country
join skills as language_skills on language_skills.skill_name = customers.language
(2) You need to restrict your results for just the sales manager you want, e.g. sales manager with id = 11:
where country_skills.sales_manager_id = 11
and language_skills.sales_manager_id = 11
(3) The 'dynamic' order you want:
order by country_skills.skill_value desc, language_skills.skill_value desc, regTime desc
This would be my complete query:
select *
from customers
join skills as country_skills on country_skills.skill_name = customers.country
join skills as language_skills on language_skills.skill_name = customers.language
where country_skills.sales_manager_id = 11
and language_skills.sales_manager_id = 11
order by country_skills.skill_value desc, language_skills.skill_value desc, regTime desc
So you got all customers sorted by country skills of a specific sales manager > language skills of a specifiy sales manager > regTime of customer.
This query ignores customers with a country or language the sales manager got no skill... you can avoid that with a LEFT JOIN

Calculate a variable using 2 Mysql tables and make a select based on that variable

I own an online game in which you become the coach of a rugby team and I recently started to optimize my database. The website uses CodeIgniter framework.
I have the following tables (the tables have more fields but I posted only those which are important now):
LEAGUES: id
STANDINGS: league_id, team_id, points
TEAMS: id, active
Previously, I was having in the LEAGUES table a field named teams. This was representing the number of active teams in that league (of which users logged in recently).
So, I was doing the following select to get a random league that has between 0 and 4 active teams (leagues with less teams first).
SELECT id FROM LEAGUES WHERE teams>0 AND teams<4 ORDER BY teams ASC, RAND( ) LIMIT 1
Is there any way I can do the same command now without having to add the teams field?
Is it efficient? Or It's better to keep the teams field in the database?
LATER EDIT
This is what I did until now:
function test()
{
$this->db->select('league_id, team_id');
$this->db->join('teams', 'teams.id = standings.team_id');
$this->db->where('active', 0);
$query = $this->db->get('standings');
return $query->result_array();
}
The function returns all inactive teams alongside with their league_id.
Now how do I count the number of inactive teams in each league and how to I sort them after this number?
Try this:
select league_id
from standings s
join teams t on t.id = s.team_id and t.active
group by 1
having count(*) < 5