Using ALL with MySQL - mysql

I have three tables: Suppliers (id, name...), TypeOfServices(id, service...) and SupplierService(supplierId, ServiceId).
As search input, I have a list with type of service ids. I can have (1) or (1,3) or (1,2,3). I need a query in MySQL that will return to me all the Suppliers that provide the services that are in the search (it can provide other services as well, but it MUST provide the ones that are in the search).
I can't use IN, because IN(2,3) will return the suppliers that have serviceId 2 OR 3, and I need 2 AND 3.
I tried with ALL like this:
SELECT * FROM suppliers as s
INNER JOIN supplierservices as ss ON ss.SupplierId = s.Id
WHERE ss.ServiceId = ALL (SELECT id FROM typeofservices WHERE id IN (2, 3))
but it's not giving me any results and I have a supplier that provides both services (with id 2 and 3). I think that SQLServer accepts the query like this ALL(2,3), but in MySQL it's not working like that, you have to have a query inside ALL.

Try
SELECT * FROM suppliers as s
INNER JOIN supplierservices as ss ON ss.SupplierId = s.Id
WHERE ss.ServiceId IN(2,3)
GROUP BY s.id
HAVING COUNT (DISTINCT ServiceId)=2

You can do this as:
SELECT s.*
FROM suppliers s INNER JOIN
supplierservices ss
ON ss.SupplierId = s.Id
GROUP BY s.id
HAVING sum(id = 2) > 0 and
sum(id = 3) > 0;
Just add a new condition to the having clause for each value you need.

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

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

Select values from table with multiple attributes

I have 3 tables:
1: products (id, name)
2: product_attributes (attribute_id, name)
3: product_attributes_selected (product_id, attribute_id,value)
Now I want to get all product_id that have two or more desired attributes and values
How can I accomplish that?
I tried this, but it failed:
select p.id,p.nazwa_pl
from produkty p,produkty_atrybuty_wartosci paw
where (paw.atrybut_id=2 and paw.wartosc=4)
and (paw.atrybut_id=3 and paw.wartosc=0)
and p.id=paw.produkt_id
group by p.id
I'n my example we have a table named Attribs, some how you need to create (e.g. TABLE variable, or CTE or some other way), my code will show you how to JOIN it.
CREATE TABLE Attribs( Attrib INT, Val INT )
^^ Populate some data in it... (again could be automated) ^^
select p.id,p.nazwa_pl
from produkty p
JOIN produkty_atrybuty_wartosci paw
ON p.id=paw.produkt_id
JOIN Attribs AS A
ON A.Attrib = paw.atrybut_id
AND A.Val = paw.wartosc
With the following query you get all ids. The idea is the following: you get all products that have the attribute 2 OR 3 OR 4. Afterwards, you group the result by the product id and count the grouped items (grouped). Only those entries that group 3 entries (you are searching for 3 attribute ids) are sufficient. Obviously, the resulting products can have more attributes, but you asked for at least the provided attributes.
SELECT p.id, p.nazwa_pl, paw.bibkey, paw.keywordid, COUNT(*) as grouped
FROM produkty p, produkty_atrybuty_wartosci paw
WHERE p.id=paw.produkt_id AND paw.atrybut_id IN (2, 3, 4) GROUP BY p.id
HAVING grouped = 3;
Having a table1 resource with columns {'id','name',....} and a table2 resource_attribute with columns {'id','resource_id','attribute_name','attribute_value'} where column resource_id is a foreign key pointing to Resource.id, you can do it in this way:
SELECT r.* FROM resource r LEFT JOIN resource_attribute ra ON r.id = ra.resource_id
AND (
(ra.NAME = 'height' AND ra.VALUE = '20') #First attribute
OR
(ra.NAME = 'color' AND ra.VALUE = 'blue') #Second attribute
)
GROUP BY r.id
HAVING COUNT(*) = 2 #Total number of attributes searched
of course if you want to make it dynamic, and use the attributes name and values you need, you can save the number of attributes you want to use in your query and reuse it in the final COUNT(*) function.
I use this query dynamic in my code (java) and it works.
Actualy I found ansfer that works for me
SELECT p.id,p.nazwa_pl FROM produkty p JOIN produkty_atrybuty_wartosci paw ON p.id=paw.produkt_id WHERE (paw.atrybut_id,paw.wartosc) IN ((2,4),(3,0)) GROUP BY p.id, p.nazwa_pl HAVING COUNT(DISTINCT paw.atrybut_id)=2
Thanks for Your help and time

Where clause in Query

My query looks like this. Suppose it was run well.im confused the last part of the Where clause.Can i write that from two different table?..how can i write it, cause i want to display those staff who a Active from that date range.
select d.Division,a.FirstName,
(select count(h.id) from Department h
inner join institution i on d.institution_id = i_Id
----
----
where i.institution_id =d.Id and h. date between #startDate and #endDate) as test
from Division d, inmate a
where d.Active = 1 and a.Active = 1
edited
i have edit my query and the final looks like this..
select d.DivisionName,a.FirstName, (select count(h.id) from InHistory h inner join Institution i on h.Institution_id = i.Id inner join InType it on h.InType_id = it.Id inner join mate a on h.mate_id = a.Id where i.InstitutionRegion_id = d.Id and it.InTypeName like '%Staff%' and h.AdmissionDate between '18/02/2013' and '18/02/2013') as Admission from Division d, mate a where d.Active= 1 and a.Active =1
Yes, you can give comparison between any number of tables in where clause provided you are giving valid conditions inside your where clause. I think you should refer SQL JOINS
You can add as many as you want clauses inside the WHERE from your SQL query..
see an example
SELECT *
FROM employee inner join department on
employee.DepartmentID = department.DepartmentID;
WHERE
employee.active = TRUE AND
department.group = 3
http://en.wikipedia.org/wiki/Join_(SQL)#Inner_join

MS Access - Aggregate functions and max

I have this query, where I am trying to get max age of a retail store seller(There's multiple towns), and show multiple if there's multiple people with the same (max)age. I am using Microsoft Access 2010. Here is the query:
SELECT Linnad.Linn, Myyjad.Nimi, Max(Myyjad.Vanus) As Vanus
FROM Linnad INNER JOIN Myyjad ON Linnad.LinnID = Myyjad.LinnID
GROUP BY Linnad.Linn, Myyjad.Nimi
ORDER BY Linnad.Linn;
The problem is, it seems to ignore the MAX, and just shows all of the values, and I can't remove the group by Myyjad.Nimi, because it gives me an error that aggregate function not included for Myyjad.Nimi.
And the output should be:
Town - Name - Max(Age)
Also, Linn = Town, Nimi = Name and the Vanus = Age.
I think this may be what your looking for:
SELECT L.Linn, M.Nimi, M.Vanus
FROM Linnad As L,
(
SELECT M2.LinnID, M2.Nimi, M2.Vanus
FROM Myyjad As M2
WHERE M2.Vanus = (SELECT Max(Z.Vanus) FROM Myyjad As Z WHERE Z.LinnID = M2.LinnID)
) As M
WHERE M.LinnID = L.LinnID
This performs a sub-select to get a list of the Linn ID's with all Nimi's showing the maximum Vanus, then we link this sub-select back to the Linnad table via the LinnID.
I think you want:
SELECT Linnad.Linn, Myyjad.Nimi, Myyjad.Vanus
FROM Linnad INNER JOIN Myyjad ON Linnad.LinnID = Myyjad.LinnID
WHERE DateValue(Myyjad.Vanus)
= (SELECT Max(DateValue(Myyjad.Vanus)) FROM Myyjad)
ORDER BY Linnad.Linn
Top N per group:
SELECT Linnad.Linn, Myyjad.Nimi, Myyjad.Vanus
FROM FROM Linnad INNER JOIN Myyjad ON Linnad.LinnID = Myyjad.LinnID
WHERE Myyjad.ID In (
SELECT Top 1 m.ID
FROM Myyjad m
WHERE m.LinnID=Linnad.ID
ORDER BY m.Vanus Desc, m.ID)
Grouping by Linn (town) and Nimi (name) tells the db engine to give you one row for each combination of town and name, and show you the maximum Vanus (age) for each of those combinations. And logically, that's not what you want. You want the name of each person whose age is the same as the maximum age in that town.
First verify you can retrieve the max age for each LinnID.
SELECT
LinnID,
Max(Vanus) As MaxOfVanus
FROM
Myyjad
GROUP BY LinnID;
If that works, you can save it as "qryTownAge", then use it in another query where you join it (on LinnID) with Linnad. That will allow you to retrieve the matching Linn.
SELECT l.LinnID, l.Linn, q.MaxOfVanus
FROM
Linnad AS l
INNER JOIN qryTownAge AS q
ON l.LinnID = q.LinnID
ORDER BY l.Linn;
If that works, save it as qryTownAge2. Then try this query.
SELECT q.Linn, q.MaxOfVanus, m.Nimi
FROM
qryTownAge2 AS q
INNER JOIN Myyjad AS m
ON (
m.LinnID = q.LinnID
AND m.Vanus = q.MaxOfVanus
)
ORDER BY q.Linn;
If that all works, you could create a single query which does it all. However, doing it step by step should help us pinpoint errors.