For the table:
assistants_rating(
id INT PRIMARY KEY auto_increment,
assistant_id INT(11),
rating INT(1)
)
And another table of assistants which contains a name. My query goes as follows:
(SELECT assistants.name AS assist_name , count(rating) AS OneStar FROM assistants_rating
INNER JOIN assistants on assistants.assistant_id = assistants_rating.assistant_id WHERE rating = 1)
UNION
(SELECT assistants.name AS assist_name , count(rating) AS TwoStar FROM assistants_rating
INNER JOIN assistants on assistants.assistant_id = assistants_rating.assistant_id WHERE rating = 2)
UNION
(SELECT assistants.name AS assist_name , count(rating) AS ThreeStar FROM assistants_rating
INNER JOIN assistants on assistants.assistant_id = assistants_rating.assistant_id WHERE rating = 3)
GROUP BY assistants.name,OneStar,TwoStar,ThreeStar;
Let's say I have the assistants named Bob and Sophie, the query should return:
assist_name OneStar TwoStar ThreeStar
Bob 9 18 52
Sophie 15 8 61
But instead I'm getting a SQL syntax error, which is weird because when I do each of them alone , they work properly and that leads me to believe that my syntax is good. Not only that, but my queries with the UNION work if I only do TwoStar and ThreeStar together but not with OneStar and TwoStar.
I'm so confused; why doesn't this work?
UNION doesn't help here anyway. It does a different thing.
The query you need is something along the lines of:
SELECT
assistants.name AS assist_name,
SUM(IF(rating = 1, 1, 0)) AS OneStar,
SUM(IF(rating = 2, 1, 0)) AS TwoStar,
SUM(IF(rating = 3, 1, 0)) AS ThreeStar
FROM assistants_rating
INNER JOIN assistants ON assistants.assistant_id = assistants_rating.assistant_id
GROUP BY assistants.name
You can use CASE instead of Union
SELECT assistants.name AS assist_name,
SUM(CASE WHEN rating =1 THEN 1 ELSE 0 END) AS OneStar,
SUM(CASE WHEN rating =2 THEN 1 ELSE 0 END) AS TwoStar,
SUM(CASE WHEN rating =3 THEN 1 ELSE 0 END) AS ThreeStar
FROM assistants_rating
INNER JOIN assistants on assistants.assistant_id = assistants_rating.assistant_id
GROUP BY assistants.name
my mySQL (pun intended) is a bit rusty. I am trying to join a table through another table.
carparks has many clients
clients has many cars
This is the query
select `carparks`.* from `carparks`
left join `clients` on `carparks`.`carpark_id` = `clients`.`carpark_id`
left join `cars` on `clients`.`client_id` = `cars`.`client_id`
where `carparks`.`carpark_id` in (1, 3, 8, 33, 34, 38, 39)
order by `cars`.`created_at` desc
As you can see I am trying to order by the created_at column of cars, the above query though returns duplicated carparks for each of the cars within the carpark.
What I am looking at is to return only those carparks with the ids in the WHERE IN clause, simply ordered by the created_at column of the cars table.
Thanks
You can use aggregation in your order by clause on max created date from cars table
SELECT cp.*
FROM `carparks` cp
LEFT JOIN `clients` cl ON cp.`carpark_id` = cl.`carpark_id`
LEFT JOIN `cars` c ON cl.`client_id` = c.`client_id`
WHERE cp.`carpark_id` IN (1, 3, 8, 33, 34, 38, 39)
GROUP BY cp.`carpark_id`
ORDER BY MAX(c.`created_at`) DESC
Reduce the wanted dates to one per carpark before joining back to carparks. Note if a carpark does have no cars than a left join is logical, however I expect every carpark (that is open for business) will have cars, so that left join might not be needed.
SELECT `carparks`.*
FROM `carparks`
LEFT JOIN (
SELECT
`carparks`.`carpark_id`
, max(`cars`.`created_at`) max_car_created
FROM `clients`
INNER JOIN `cars` ON `clients`.`client_id` = `cars`.`client_id`
GROUP BY
`carparks`.`carpark_id`
) d ON `carparks`.`carpark_id` = d.`carpark_id`
WHERE `carparks`.`carpark_id` IN (1, 3, 8, 33, 34, 38, 39)
ORDER BY max_car_created DESC
Reduce the number of carparks and clients before doing the joins, this will reduce the execution time of the query.
SELECT A.* FROM (SELECT * FROM `carparks` WHERE `carpark_id` in
(1, 3, 8, 33, 34, 38, 39)) A LEFT JOIN
(SELECT `carpark_id`, `client_id` FROM `clients` WHERE `carpark_id`
in (1, 3, 8, 33, 34, 38, 39)) B ON A.`carpark_id`=B.`carpark_id` LEFT JOIN
`cars` C ON B.`client_id` = C.`client_id`
GROUP BY A.`carpark_id`
ORDER BY MAX(C.`created_at`) DESC
I have a query that I want to optimize. Both unions executed separatley run fine, however as soon as I include inner join it takes up to 57 seconds. How do I solve this. My query is as follows
SELECT
p.PROJID,
p.StartDate,
o.ORDERNO,
p.PROJCODE,
p.PROJECT,
cat.type AS CATEGORY,
p.AREA,
p.STATE,
p.COUNTRY,
p.VALUE,
p.PROCESSOR,
p.PROJINFO,
p.NES,
p.SPECSALE,
p.OFFICE,
p.DEPTCODE,
p.INTERNLCHG,
p.INTERCOCHG,
p.LORM,
p.PERCENT,
d.COMPANY,
CONCAT(
d.LASTNAME,
", ",
d.FIRSTNAME
) AS Contact
FROM
(
(
SELECT
*
FROM
(
SELECT
`clients`.`CLIENTID` AS `CLIENTIDA`,
`clients`.`COMPANY` AS `COMPANY`
FROM
`hdb`.`clients`
UNION
SELECT
`accounts`.`id` AS `CLIENTIDA`,
`accounts`.`name` AS `COMPANY`
FROM
`sugarcrm`.`accounts`
) AS a
INNER JOIN (
SELECT
`hdb`.`contacts`.`CONTACTID` AS `CONTACTID`,
`hdb`.`contacts`.`CLIENTID` AS `CLIENTIDC`,
`hdb`.`contacts`.`FIRSTNAME` AS `FIRSTNAME`,
`hdb`.`contacts`.`LASTNAME` AS `LASTNAME`
FROM
`hdb`.`contacts`
UNION
SELECT
`sugarcrm`.`contacts`.`id` AS `CONTACTID`,
`sugarcrm`.`accounts_contacts`.`account_id` AS `CLIENTIDC`,
`sugarcrm`.`contacts`.`first_name` AS `FIRSTNAME`,
`sugarcrm`.`contacts`.`last_name` AS `LASTNAME`
FROM
`sugarcrm`.`contacts`
LEFT JOIN `sugarcrm`.`accounts_contacts` ON `sugarcrm`.`contacts`.`id` = `sugarcrm`.`accounts_contacts`.`contact_id`
) AS c ON a.CLIENTIDA = c.CLIENTIDC
) AS d
)
INNER JOIN (
(
projects AS p
INNER JOIN orders AS o ON p.ORDERNO = o.ORDERNO
)
INNER JOIN category AS cat ON p.category_id = cat.category_id
) ON d.CONTACTID = o.CONTACTID
Explain on this provides the following:
1, PRIMARY, cat, ALL, PRIMARY, , , , 10,
1, PRIMARY, p, ref, FK_orders_projects,FK_category_projects_idx, FK_category_projects_idx, 5, hdb.cat.category_id, 400, Using where
1, PRIMARY, o, eq_ref, PRIMARY, PRIMARY, 4, hdb.p.ORDERNO, 1,
1, PRIMARY, <derived2>, ALL, , , , , 18878, Using where
2, DERIVED, <derived3>, ALL, , , , , 7087,
2, DERIVED, <derived5>, ALL, , , , , 18879, Using where
5, DERIVED, contacts, ALL, , , , , 8261,
6, UNION, contacts, ALL, , , , , 10251,
6, UNION, accounts_contacts, ref, idx_contid_del_accid, idx_contid_del_accid, 111, sugarcrm.contacts.id, 1, Using index
, UNION RESULT, <union5,6>, ALL, , , , , ,
3, DERIVED, clients, ALL, , , , , 2296,
4, UNION, accounts, ALL, , , , , 4548,
, UNION RESULT, <union3,4>, ALL, , , , , ,
The original query without the union takes 0.125 seconds
SELECT p.PROJID, p.StartDate, o.ORDERNO, p.PROJCODE, p.PROJECT, cat.type AS CATEGORY, p.AREA, p.STATE, p.COUNTRY,
p.VALUE, p.PROCESSOR, p.PROJINFO, p.NES, p.SPECSALE, p.OFFICE, p.DEPTCODE, p.INTERNLCHG, p.INTERCOCHG, p.LORM,
p.PERCENT, a.COMPANY, CONCAT(c.LASTNAME, ", ", c.FIRSTNAME) AS Contact
FROM (clients AS a
INNER JOIN contacts AS c ON a.CLIENTID =c.CLIENTID)
INNER JOIN ((projects AS p INNER JOIN orders AS o ON p.ORDERNO = o.ORDERNO)
INNER JOIN category AS cat ON p.category_id = cat.category_id) ON c.CONTACTID = o.CONTACTID
ORDER BY p.PROJID, a.COMPANY;
explain on this provides following:
1, SIMPLE, cat, ALL, PRIMARY, , , , 10, Using temporary; Using filesort
1, SIMPLE, p, ref, FK_orders_projects,FK_category_projects_idx, FK_category_projects_idx, 5, hdb.cat.category_id, 400, Using where
1, SIMPLE, o, eq_ref, PRIMARY,FK_contacts_orders, PRIMARY, 4, hdb.p.ORDERNO, 1,
1, SIMPLE, c, eq_ref, PRIMARY,FK_clients_contacts, PRIMARY, 52, hdb.o.CONTACTID, 1,
1, SIMPLE, a, eq_ref, PRIMARY, PRIMARY, 52, hdb.c.CLIENTID, 1,
Query with view:
SELECT
p.PROJID,
p.StartDate,
o.ORDERNO,
p.PROJCODE,
p.PROJECT,
cat.type AS CATEGORY,
p.AREA,
p.STATE,
p.COUNTRY,
p.VALUE,
p.PROCESSOR,
p.PROJINFO,
p.NES,
p.SPECSALE,
p.OFFICE,
p.DEPTCODE,
p.INTERNLCHG,
p.INTERCOCHG,
p.LORM,
p.PERCENT,
a.COMPANY,
CONCAT(
c.LASTNAME,
", ",
c.FIRSTNAME
) AS Contact
FROM
(
view_accounts_sugar_hdb AS a
INNER JOIN view_contacts_sugar_hdb AS c ON a.CLIENTID = c.CLIENTID
)
INNER JOIN (
(
projects AS p
INNER JOIN orders AS o ON p.ORDERNO = o.ORDERNO
)
INNER JOIN category AS cat ON p.category_id = cat.category_id
) ON c.CONTACTID = o.CONTACTID
ORDER BY
p.PROJID,
a.COMPANY;
This takes over 340 secs.
This one was definitely uglier than the last one I helped you on :)... Anyhow, the same principles apply. For future, please do try to understand what I'm doing here. Write the JOIN relationships FIRST to know where your data is coming from. Also, take a look at my indentations... I am showing at each level for readability.
Orders -> Projects -> Categories...
then, the path for those in the normal clients table via
Orders -> Contacts -> Clients
finally to the SugarCRM contacts...
Orders -> Accounts_Contacts -> Accounts
So, now that you have the relationships set (and aliased), this follows similar of last answer implementing LEFT-JOIN to normal contact/client vs CRM Contacts/Accounts.
The field list is simple on the Order, Products and Category tables as those are pretty direct. That leaves just the "who/client" information where the LEFT-JOIN comes in. If the Normal client is null, use the CRM version fields, otherwise use the normal client fields...
SELECT
P.PROJID,
P.StartDate,
O.ORDERNO,
P.PROJCODE,
P.PROJECT,
cat.`type` AS CATEGORY,
P.AREA,
P.STATE,
P.COUNTRY,
P.VALUE,
P.PROCESSOR,
P.PROJINFO,
P.NES,
P.SPECSALE,
P.OFFICE,
P.DEPTCODE,
P.INTERNLCHG,
P.INTERCOCHG,
P.LORM,
P.PERCENT,
CASE when HCLIENT.ClientID IS NULL
then SCLIENT.`name`
ELSE HCLIENT.Company end as Company,
CASE when HCLIENT.ClientID IS NULL
then CONCAT( SCT.LAST_NAME, ", ", SCT.FIRST_NAME )
ELSE CONCAT( HCT.LASTNAME, ", ", HCT.FIRSTNAME ) end as Contact
FROM
orders O
JOIN projects P
ON O.OrderNo = P.OrderNo
JOIN category AS cat
ON p.category_id = cat.category_id
LEFT JOIN hdb.contacts HCT
ON O.ContactID = HCT.ContactID
LEFT JOIN hdb.clients HCLIENT
ON HCT.ClientID = HCLIENT.ClientID
LEFT JOIN sugarcrm.contacts SCT
ON O.ContactID = SCT.ID
LEFT JOIN sugarcrm.accounts_contacts SAC
ON SCT.ID = SAC.contact_id
LEFT JOIN sugarcrm.accounts SCLIENT
ON SCT.account_id = SCLIENT.ID
I'd be interested in the performance improvement too.
Given two tables:
The 'people' table contains the following columns:
name
favorite_walking_shoe
favorite_running_shoe
favorite_dress_shoe
favorite_house_shoe
favorite_other_shoe
The 'shoes' table contains the following columns:
shoe
name
description
I want to create a result set that contains:
people.name, people.favorite_shoe_type, shoes.name, shoes.description
I know I can get the desired results using something like:
select p.name, p.favorite_shoe_type, s.name, s.description
from (select name, favorite_walking_shoe as shoe, 'walking' as favorite_shoe_type
from people where favorite_walking_shoe is not null
union all
select name, favorite_running_shoe, 'running'
from people where favorite_running_shoe is not null
union all
select name, favorite_dress_shoe, 'dress'
from people where favorite_dress_shoe not is null
union all
select name, favorite_house_shoe, 'house'
from people where favorite_house_shoe not is null
union all
select name, favorite_other_shoe, 'other'
from people where favorite_other_shoe not is null
) p
join shoes s on s.shoe = p.shoe
order by 1,2
but this would require 5 passes of the 'people' table. Is there a way to accomplish the UNION ALLs without requiring multiple passes?
I should point out that the structures are part of a vendor product which I cannot modify. :(
You can get around the five scans by doing a cross join:
select p.name, p.favorite_shoe_type, s.name, s.description
from (select p.*,
(case when favorite_shoetype = 'walking' then p.favore_walking_shoe
when favorite_shoetype = 'running' then p.favorite_running_shoe
when favorite_shoetype = 'dress' then p.favorite_dress_shoe
when favorite_shoetype = 'house' then p.favorite_house_shoe
when favorite_shoetype = 'other' then p.favorite_other_shoe
end) as shoe
from people p cross join
(select 'walking' as favorite_shoe_type union all
select 'running' union all
select 'dress' union all
select 'house' union all
select 'other'
) shoetypes join
shoes s
) p
on s.shoe = p.shoe
I'm not sure this will be more efficient. If you have indexes on shoe, this even more complicated version might be more efficient:
select p.name, p.favorite_shoe_type, s.name, s.description
from (select p.name, favorite_shoe_types,
(case when favorite_shoetype = 'walking' then ws.name
when favorite_shoetype = 'running' then rs.name
when favorite_shoetype = 'dress' then ds.name
when favorite_shoetype = 'house' then hs.name
when favorite_shoetype = 'other' then os.name
end) as name,
(case when favorite_shoetype = 'walking' then ws.description
when favorite_shoetype = 'running' then rs.description
when favorite_shoetype = 'dress' then ds.description
when favorite_shoetype = 'house' then hs.description
when favorite_shoetype = 'other' then os.name
end) as description
from people p left outer join
shoes ws
on ws.shoe = favorite_walking_shoe left outer join
shoes rs
on rs.shoe = favorite_running_shoe left outer join
shoes ds
on ds.shoe = favorite_dress_shoe left outer join
shoes hs
on hs.shoe = favorite_house_shoe left outer join
shoes os
on os.shoe = favorite_other_shoe cross join
(select 'walking' as favorite_shoe_type union all
select 'running' union all
select 'dress' union all
select 'house' union all
select 'other'
) shoetypes
) p
on s.shoe = p.shoe
where s.name is not null
This should do the five joins using indexes -- quite fast, one scan of the people table, and feed this to the cross join. The logic then returns the values that you want.
Note: both of these are untested so they might have syntax errors.
Unfortunately, the way your current table is structured you will have multiple passes to get each value. If it is possible, I would suggest changing your table structure to the include a shoe_type table and then a join table between the people and shoes and in this table, you can include a flag that will show if the shoe is the favorite.
So it will be similar to this:
create table people_shoe
(
people_id int,
shoe_id int,
IsFavorite int
);
You could also have a shoe_type table to store each of the different show types:
create table shoe_type
(
id int,
name varchar(10)
);
insert into shoe_type
values('Walking'), ('Running'), ('Dress'), ('House'), ('Other');
The shoe_type.id would be added to your shoe table and you would join the tables.
Edit #1, if you can remodel the database, you could use the following (mock-up model):
create table people
(
id int,
name varchar(10)
);
insert into people values (1, 'Jim'), (2, 'Jane');
create table shoe_type
(
id int,
name varchar(10)
);
insert into shoe_type
values(1, 'Walking'), (2, 'Running'), (3, 'Dress'), (4, 'House'), (5, 'Other');
create table shoes
(
id int,
name varchar(10),
description varchar(50),
shoe_type_id int
);
insert into shoes
values(1, 'Nike', 'test', 2), (2, 'Cole Haan', 'blah', 3);
create table people_shoe
(
people_id int,
shoe_id int,
IsFavorite int
);
insert into people_shoe
values (1, 1, 1),
(1, 2, 0),
(2, 1, 1);
Then when you query, your code will be similar to this:
select p.name PersonName,
s.name ShoeName,
st.name ShoeType,
ps.isFavorite
from people p
inner join people_shoe ps
on p.id = ps.people_id
inner join shoes s
on ps.shoe_id = s.id
inner join shoe_type st
on s.shoe_type_id = st.id
See SQL Fiddle with Demo
Given two tables, one for workers and one for tasks completed by workers,
CREATE TABLE IF NOT EXISTS `workers` (
`id` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `workers` (`id`) VALUES
(1);
CREATE TABLE IF NOT EXISTS `tasks` (
`id` int(11) NOT NULL,
`worker_id` int(11) NOT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `tasks` (`id`, `worker_id`, `status`) VALUES
(1, 1, 1),
(2, 1, 1),
(3, 1, 2),
(4, 1, 2),
(5, 1, 2);
I'm trying to get the number of tasks each worker has with each status code.
I can say either
SELECT w.*
,COUNT(t1.worker_id) as status_1_count
FROM workers w
LEFT JOIN tasks t1 ON w.id = t1.worker_id AND t1.status = 1
WHERE 1
GROUP BY
t1.worker_id
ORDER BY w.id
or
SELECT w.*
,COUNT(t2.worker_id) as status_2_count
FROM workers w
LEFT JOIN tasks t2 ON w.id = t2.worker_id AND t2.status = 2
WHERE 1
GROUP BY
t2.worker_id
ORDER BY w.id
and get the number of tasks with a single given status code, but when I try to get the counts for multiple task statuses in a single query, it doesn't work!
SELECT w.*
,COUNT(t1.worker_id) as status_1_count
,COUNT(t2.worker_id) as status_2_count
FROM workers w
LEFT JOIN tasks t1 ON w.id = t1.worker_id AND t1.status = 1
LEFT JOIN tasks t2 ON w.id = t2.worker_id AND t2.status = 2
WHERE 1
GROUP BY t1.worker_id
,t2.worker_id
ORDER BY w.id
The tasks table is cross-joining against itself when I would rather it wouldn't!
Is there any way to combine these two queries into one such that we can retrieve the counts for multiple task statuses in a single query?
Thanks!
SELECT w.*,
SUM(t1.status = 1) AS status_1_count,
SUM(t1.status = 2) AS status_2_count
FROM workers w
LEFT JOIN tasks t1 ON w.id = t1.worker_id AND t1.status IN (1, 2)
GROUP BY w.id
ORDER BY w.id;
I'm trying to get the number of tasks each worker has with each status code.
SELECT worker_id, status, COUNT(*)
FROM tasks
GROUP BY worker_id, status;
That's all.
I don't have an instance of MySQL here, but I tested this on a t-sql box and it worked.
select distinct(worker_id),
(select count(*) from tasks where status = 1) as Status1,
(select count(*) from tasks where status = 2) as Status2
from tasks;