Is this sql query correct? - mysql

Using table below
http://i.imgur.com/rIMgFZC.png
How do i display the names of toys that processed by Female Employees who are in level 3, level 4, and level 5 (not level 1 or 2) and a list of all toys’ name with stores’ postcode 10005. Write using union.
SELECT Toy_name
FROM Toy T
INNER JOIN hire_transaction H on T.toy_id = H.toy_id
INNER JOIN Employee E on H.E_id = E.E_id
WHERE E_Sex = ‘F’
AND E_Level between ‘3’ and ‘5’
UNION
SELECT Toy_name, Store_id
FROM Toy T, Store S
WHERE T.Store_ID IN(
SELECT Store_ID
FROM STORE S
WHERE Store_Postcode = ‘10005’);
this is my attempt. am i correct?

SELECT Toy_name
FROM Toy T
INNER JOIN hire_transaction H on T.toy_id = H.toy_id
INNER JOIN Employee E on H.E_id = E.E_id
WHERE E_Sex = 'F'
AND E_Level between 3 and 5
UNION
SELECT Toy_name
FROM Toy T
WHERE T.Store_ID IN(
SELECT Store_ID
FROM STORE S
WHERE Store_Postcode = '10005');
When using unions the distinct selects must have the same number and types of columns.
Removed the unnecessary implicit join in your second query.
You used the wrong quotation marks, inserted the single quotes.

Related

Need count of transactional table based on other tables including zeros where there are no matches

I have four tables, three of which are pretty static: haul_types, dumpster_type_team (the dumpster_type_team has the many-to-many relationship between dumpster_types and teams), and users. The fourth table, hauls, has transactional data.
haul_types:
id
name
dumpster_type_team:
id
dumpster_type_id
team_id
users:
id
first_name
last_name
is_driver
team_id
hauls:
haul_type_id
haul_status_id
set_dumpster_type_id
completed_driver_id
team_id
I would like a query that has a combination of dumpster_types, haul_types, and drivers (users) and a count of the hauls they were involved in. In some cases, there should be a count of zero because some drivers haven't completed hauls for every haul_type / dumpster type combination.
Here's the query I have so far that seems to be behaving as if it is an inner join because the records are getting filtered to only show where there are matches:
SELECT
c.haul_type_id,
c.dumpster_type_id,
c.driver_id,
count(h.id) AS haul_count
FROM
hauls h
RIGHT JOIN ( SELECT DISTINCT
ht.id AS haul_type_id,
dtt.dumpster_type_id AS dumpster_type_id,
dtt.team_id AS team_id,
u.id AS driver_id
FROM
haul_types ht
CROSS JOIN dumpster_type_team dtt
CROSS JOIN users u
WHERE
u.team_id = dtt.team_id
AND u.is_driver = TRUE) c ON c.haul_type_id = h.haul_type_id
AND c.dumpster_type_id = h.set_dumpster_type_id
AND c.driver_id = h.completed_driver_id
AND c.team_id = h.team_id
WHERE
h.team_id = 9
AND h.haul_status_id = 3
AND h.completed_driver_id IS NOT NULL
GROUP BY
c.haul_type_id, c.dumpster_type_id, c.driver_id
When I run the subquery in isolation:
SELECT DISTINCT
ht.id AS haul_type_id,
dtt.dumpster_type_id AS dumpster_type_id,
dtt.team_id AS team_id,
u.id AS driver_id
FROM
haul_types ht
CROSS JOIN dumpster_type_team dtt
CROSS JOIN users u
WHERE
u.team_id = dtt.team_id
AND u.is_driver = TRUE
I get the results I want: a row for each permutation of haul_type, dumpster_type, driver_id, and team_id. However, when I run the entire query, I get filtered results despite the right join.
What I would like to have is the following:
If I have 4 haul_types: delivery, swap, live, pickup
and 2 dumpster_types: 10YD, 15YD
and 2 drivers: 1, 2
I would like a haul count for the combination of haul_type, dumpster_type, and driver. If there are no hauls matching the row, show 0:
Any help is appreciated. Thank you
The description of the question and the query seem to have little to do with each other. I don't know what a "pivot table" is supposed to be.
I would like a query that has a combination of dumpster_types, haul_types, and drivers (users) and a count of the hauls they were involved in.
This sounds like a cross join to generate the rows and then a left join/group by to calculate the results:
select d.dumpster_id, ht.haul_type_id, d.driver_id, count(h.driver_id)
from dumpster_types d cross join
haul_types ht cross join
drivers d left join
hauls h
on h.dumpster_id = d.dumpster_id and
h.haul_type_id = ht.haul_type_id and
h.driver_id = d.driver_id
group by d.dumpster_id, ht.haul_type_id, d.driver_id;
Running the query #GordonLinoff provided, exposed the issue I was facing - when applying a where clause on the top level query, the results were getting filtered to only matches. I moved the where clause to individual subqueries and now I am getting all expected results.
Not sure if this is the most efficient way to write it but it yields the correct results:
SELECT
d.dumpster_type_id,
ht.id AS haul_type_id,
u.id AS driver_id,
count(h.id) AS haul_count
FROM (
SELECT
dumpster_type_id,
team_id
FROM
dumpster_type_team
WHERE
team_id = 9) d
CROSS JOIN haul_types ht
CROSS JOIN (
SELECT
users.id
FROM
users
WHERE
users.is_driver = TRUE
AND users.team_id = 9) u
LEFT JOIN (
SELECT
id, set_dumpster_type_id, haul_type_id, completed_driver_id, team_id
FROM
hauls
WHERE
haul_status_id = 3
AND team_id = 9) h ON h.set_dumpster_type_id = d.dumpster_type_id
AND h.haul_type_id = ht.id
AND h.completed_driver_id = u.id
AND h.team_id = d.team_id
GROUP BY
d.dumpster_type_id,
ht.id,
u.id

How do I get the MIN value from two columns along with corresponding id?

My query does return the lowest price from the two columns (price_base, price_special) but it is not returning the correct store_id that corresponds to the lowest price found.
My Query:
SELECT grocery_item.id, grocery_item.category,
grocery_category.name AS cat, grocery_item.name AS itemName,
MIN( if( grocery_price.price_special>0,
grocery_price.price_base)) AS price,
grocery_price.store_id,
grocery_store.name AS storeName
FROM grocery_item
LEFT JOIN grocery_category ON
grocery_category.id=grocery_item.category
LEFT JOIN grocery_price
ON grocery_price.item_id = grocery_item.id
LEFT JOIN grocery_store
ON grocery_store.id=grocery_price.store_id
WHERE grocery_price.selection='no'
AND buy='yes'
GROUP BY grocery_price.item_id
ORDER BY store_id, grocery_item.category, grocery_item.name
Returns this:
ID category cat itemName price store_id storeName
92 3 Bread/Bakery Arnold Bread 2.14 1 Food Lion
But the grocery_price table holds this info:
item_id price_base price_special store_id
92 4.29 2.14 9
92 3.99 0.00 1
so the store_id I need to be returned is 9 (the storeName returned would NOT then be Food Lion)
EDIT: WORKING QUERY based on Uueerdo's comments (thank you!)
SELECT minP.item_id, gi.category, gc.name AS cat,
gi.name as itemName, gp.store_id,
gs.name AS storeName, minP.price
FROM
(SELECT p.item_id, MIN(IF(p.price_special >0,
p.price_special,p.price_base)) AS price
FROM grocery_item AS i
INNER JOIN grocery_price AS p ON (i.id = p.item_id)
WHERE i.buy = 'yes'
GROUP BY p.item_id) AS minP
INNER JOIN grocery_item AS gi ON minP.item_id = gi.id
INNER JOIN grocery_category AS gc on gi.category = gc.id
LEFT JOIN grocery_price AS gp
ON minP.price = IF(gp.price_special > 0,
gp.price_special,gp.price_base)
AND gp.item_id = gi.id
INNER JOIN grocery_store AS gs ON gp.store_id = gs.id
GROUP BY gi.id
ORDER BY gs.id, gi.category,gi.name
The values returned for non-grouped, non-aggregated fields are an (effectively) random selection from the values encountered with the grouped fields' values. Most RDBMS do not even consider such a query valid, and even newer versions of MySQL default to disallowing such queries.
In cases like yours, where you need the non-grouped value(s) associated with the aggregate result (min in this case); the aggregating query must be converted into a subquery, that can be joined back to the aggregated tables to find the source row(s) that correspond to the aggregated value.
Edit: Basically, you need to look at the problem slightly differently. You're currently finding the lowest price for an item and a store that item is listed for; you need to find the lowest price for an item, and use that to find the store(s) that have the item at that price.
This gets you the lowest price for item's marked "buy":
SELECT p.item_id, MIN(IF(p.price_special > 0,p.price_special,p.price_base)) AS price
FROM grocery_item AS i
INNER JOIN grocery_price AS p ON (i.id = p.item_id)
WHERE i.buy = 'yes'
GROUP BY p.item_id
You can then take that to get the rest of the results:
SELECT minP.item_id
, gi.name
, gi.category, gc.category_name AS cat, gi.Name as itemName, gi.buy
, gp.store_id, gp.name AS storeName
, minP.price
FROM ([the query above]) AS minP
INNER JOIN grocery_item AS gi ON minP.item_id = gi.id
INNER JOIN grocery_category AS gc on gi.category = gc.grocery_category_id
/* Guessing on this join since grocery_category_id
was not qualified with it's table name */
INNER JOIN grocery_price AS gp
ON minP.price = IF(gp.price_special > 0,gp.price_special,gp.price_base)
/* Alternatively: ON minP.price IN (gp.price_special, gp.price_base)
... though this could cause false positives if the minP.price is 0
from one store's base price being "free"
*/
INNER JOIN grocery_store AS gs ON gp.store_id = gs.id
;

SQL select by qualifying multiple strings

I have to find student names who are enrolled in a class AND also in another class. But when I do a WHERE statement it cuts out out all the other lines that have the other classes.
So my question is: How would I go about doing it so that it compares the entire group and only selects students that are both in CLASSA and CLASSB.
Some of the students are in CLASSA and some of them are in CLASSB and some are in both. How do I get the one that is in both?
For example:
John Smith is in CLASSA
BETTY WHITE is in CLASSB
Timmy Edwards is in CLASSA
Timmy Edwards is in CLASSB
If I use an OR, it will select all of the names. But I only want a student that is in BOTH classes. So Timmy Edwards is the one I want selected.
WHERE C.DESCRIPTION LIKE 'Systems Analysis%'
AND C.DESCRIPTION LIKE 'Project Management%'
You can do use group by and having:
select student
from enrollments
where class in ('CLASSA', 'CLASSB')
group by student
having count(*) = 2;
This version assumes that a student can be enrolled only once for a given class.
No individual row will every have information for both classes. That means that AND and OR as you're using them are useless. You need to check for the existence of such rows using the EXISTS keyword.
select distinct
student
from
enrollments
where
exists (
select
1
from
enrollments e1
where
e1.student = enrollments.student
and e1.class = 'CLASSA'
) and exists (
select
1
from
enrollments e2
where
e2.student = enrollments.student
and e2.class = 'CLASSB'
)
Note:
Each row will have the student name in it, so I've added the DISTINCT keyword to remove the duplicates.
You could try something like this, (although im not sure what exactly your schema is like...)
WITH a AS
(
SELECT
S.FIRST_NAME||' '||S.LAST_NAME [Student]
,C.DESCRIPTION
FROM STUDENT S
JOIN ENROLLMENT E ON (E.STUDENT_ID = S.STUDENT_ID)
JOIN SECTION SE ON (SE.SECTION_ID = E.SECTION_ID)
JOIN COURSE C ON (C.COURSE_NO = SE.COURSE_NO)
WHERE C.DESCRIPTION = 'CourseA'
)
,b AS
(
SELECT
S.FIRST_NAME||' '||S.LAST_NAME [Student]
,C.DESCRIPTION
FROM STUDENT S
JOIN ENROLLMENT E ON (E.STUDENT_ID = S.STUDENT_ID)
JOIN SECTION SE ON (SE.SECTION_ID = E.SECTION_ID)
JOIN COURSE C ON (C.COURSE_NO = SE.COURSE_NO)
WHERE C.DESCRIPTION = 'CourseB'
)
Select a.Student, a.Description [DescA], b.Description [DescB]
from a join b on a.Student = b.Student
--optional where to just get students in both classes...
where a.Description is not null and b.Description is not null
Hope this assists...
You can always try the neat trick of joining a SELECT result with itself and then selecting from that result.
SELECT A.NAME
FROM (SELECT S.FIRST_NAME||' '||S.LAST_NAME as NAME,
S.STUDENT_ID as S_ID,
C.DESCRIPTION as DESC
FROM STUDENT S
JOIN ENROLLMENT E ON (E.STUDENT_ID = S.STUDENT_ID)
JOIN SECTION SE ON (SE.SECTION_ID = E.SECTION_ID)
JOIN COURSE C ON (C.COURSE_NO = SE.COURSE_NO)) A
JOIN (S.STUDENT_ID as S_ID, C.DESCRIPTION as DESC
FROM STUDENT S
JOIN ENROLLMENT E ON (E.STUDENT_ID = S.STUDENT_ID)
JOIN SECTION SE ON (SE.SECTION_ID = E.SECTION_ID)
JOIN COURSE C ON (C.COURSE_NO = SE.COURSE_NO)) B
ON A.S_ID = B.S_ID
WHERE A.DESC LIKE 'Systems Analysis%'
AND B.DESC LIKE 'Project Management%'
GROUP BY A.NAME

mysql query group by totals

I am using the following query to retrieve the number of events per state from 2 tables that are linked by a userID.
SELECT state,COUNT(*) AS num
FROM tableUserInfo
WHERE userID IN (SELECT userID
FROM tableEvents
WHERE conditionOne = 1
AND conditionTwo = 2)
GROUP BY state
This query works correctly. My problem is that not all states have user entries, and I need the query to return 0 for those. I was wondering if there was a method such as joining or using an in clause, that would included a set of all states, making the query return 0 for any that didn't have entries in tableEvents?
Do you have a list of states? If not then this would give a list of all the states your database knows about:
SELECT DISTINCT state FROM tableUserInfo
....and enclosing this in brackets it can be dropped in place in the query below:
SELECT s.state, IFNULL(cnt, 0) AS num
FROM list_of_states s
LEFT JOIN (
SELECT state,COUNT(*) AS cnt
FROM tableUserInfo ui
INNER JOIN tableEvents te
ON ui.userId=te.userId
WHERE conditionOne = 1
AND conditionTwo = 2
GROUP BY state
) u
ON s.state=u.state;
Although in the absence of "list_of_states" it would be more efficient to do this:
SELECT ui.state, SUM(IF(te.userId IS NULL, 0, 1)) AS cnt
FROM tableUserInfo ui
LEFT JOIN tableEvents te
ON ui.userId=te.userId
AND te.conditionOne = 1
AND te.conditionTwo = 2
GROUP BY state;
As #raymond-nijland suggested you can use Left Join to include all states.
SELECT tableUserInfo.state,COUNT(tableUserInfo.*) AS num
FROM tableUserInfo Left Join tableEvents on tableUserInfo.userID = tableEvents.userID
WHERE tableEvents.conditionOne = 1 AND tableEvents.conditionTwo = 2
GROUP BY state

What is wrong with this MySQL query (formatting left join)?

I have a query as follows:
SELECT
staff_names.staff_ID AS sid
staff_names.name AS name,
staff_names.rec_type AS rec_type,
prod_staff.specialized AS specialized,
compspec.name AS compspec_name
FROM staff_names JOIN prod_staff USING (staff_ID)
LEFT JOIN (prod_staff_compspec JOIN company_list USING (comp_ID)) compspec
USING (prod_ID, staff_ID, role_ID)
WHERE prod_staff.role_ID = 2
AND prod_staff.prod_ID = 27
AND prod_staff.asst = 'n'
AND episode IS NOT NULL
ORDER BY name
Running this as-is says there's an error near the 'compspec' alias. Removing that and changing 'compspec' to 'company_list' in the SELECT clause returns no rows, even though it should return 1 with the given values. The left join seems to be the problem, but I don't how it should be formatted.
The prod_staff table has prod_ID, staff_ID and role_ID fields. prod_staff_compspec has these and a comp_ID field. prod_staff may or may not have a matching prod_staff_compspec row, but prod_staff_compspec always has a matching company_list row.
What I want to do is retrieve a list of all staff names associated with a given role_ID and prod_ID in the prod_staff table, as well as a company name from the company_list table, if a link to such exists in the prod_staff_compspec table (only a small minority have one).
Switched to ON to define the table relations. LEFT JOIN (prod_staff_compspec JOIN company_list USING (comp_ID)) compspec is switched to 2 left join.
select a.staff_id sid, a.name, a.rec_type, b.specialized, d.name compspec_name
from staff_names a
join prod_staff b on a.staff_id = b.staff_id
left join prod_staff_compspec c on b.prod_id = c.prod_id and b.staff_id = c.staff_id and b.role_id = c.role_id
left join company_list d on c.comp_id = d.comp_id
where b.role_id = 2 and b.prod_id = 27 and b.asst = 'n' and episode is not null
order by a.name;