mysql equivalent of IN() clause doing logical AND's instead - mysql

Let's say I have a table making a junction between two tables... like this:
id_product | id_category
-----------------
11 | 22
11 | 33
12 | 22
12 | 33
I want to get id_products (distinct) according to a list of searched categories IDs.
If I use the IN() clause, the list of id_categories uses a logical OR.
How can I make a SELECT query to have logical ANDs for the list of id_categ submitted??
Example: I want all the id_products belonging to category 22 AND 33 (and possibly 5+ more Categ. IDs)
I found this answer:
Using MySQL IN clause as all inclusive (AND instead of OR)
...but the query is mixing more than 1 table... I only want a query on a single table, the junction one.

reading your link, I think it would be something like
select id_product
from yourTable
where id_category in (--your List goes Here)
group by id_product
having count(distinct id_category) = NumberOfElementsOfYourList
you should use = if only wants to get that id_category, but no others id_category.
If not, use >=

select id_product
from your_table
where id_category in (22, 33)
group by id_product
having count(distinct id_category) = 2
You can add a having clause that counts the found id_category's. If you look for 5 IDs for instance, you have to change the 2 in 5 in the having clause.

Related

select all rows with lowest product_id

i have this table and i want to select all rows with product id 105. i can easily do this if i know product id and put in where clause but i dont know product id and it increases once this id work is completed it will delete these rows and search for next lowest product id.
with order bt product_id i can do but how to ensure that all rows are listed. if i will give 100 limit then first 10-15 may be lowest after that it will start listing next higher product_id rows
here is table
id name product_id some_columns
1 dff 105 gfff
2 fg 109 ffgfgf
3 tt 106 gttytt
4 tt 105 trtr
5 trr 112 trrrt
6 rrr 111 rttttr
7 ttyt 108 ttrtrtr
8 rrrr 105 rrerer
SELECT id, name, product_id, some_columns
FROM table_name
WHERE product_id = (SELECT MIN(product_id) FROM table_name)
here you can see that lowest product_id is 105 but i dont have any control on how many times it will appear in table. in some case it may be 10 rows and in some case it may be 250 rows.
so order by product_id will not work. as giving limit will list all id initial few rows may be 105 but after than when all 105 rows are listed it will start listing all rows which is higher than 105
the best solution would be if i could use where product_id=105 but my bad luck i dont have any control on product id so cant use product id in where clause. next big problem is i want to use it efficiently so we have indexed product_id column
i was exploring min value option but i am highly doubtful about its efficiency and probable affect on mysql
so any help will be great
You can try something like
SELECT id, name, product_id, some_columns
FROM table_name
WHERE product_id = (SELECT MIN(product_id) FROM table_name)
As far as I understood you want to select all the rows that match the minimum product_id. Be it 101, 102 or whatever, it's uncontrollable, so you get the minimum product_id in the row and then you select the rows that has the current minimum product_id.
You could try using a subquery for min_id group by product
select m.*
from table_name m
inner join (
select min(id) min_id, product_id
from table_name
group by product_id
) t on t.min_id = m.id

MYSQL - SELECT all rows, but use Distinct on one column

I have a table data like this
Column_a | Column_b
a | 5
b | 25
g | 14
t | 13
b | 15
c | 04
g | 15
b | 13
in the column_a i have a lot of duplicate values, i want to be able to select all the rows from the table but if two rows have the same column_a value, i want only the row with the biggest value from colum_b in the results
exemple of the result that i am looking for :
Column_a | Column_b
a | 5
b | 25
t | 13
c | 04
g | 15
Thank you in advance
**
Update of the question
**
these are the columns i have in my table :
CRMID | user | ticket_id | | description | date | hour
what i am trying to do is to select all the rows from the table, but when two rows have the same ticket_id, i want only the newest one to appear in the results, so the row with the newest date and hour ,
Sorry for making this such complicated !
i am not a native english speaker and i find it hard to well explain the problem.
If you group by column_a then you can use aggregate functions like max() on it to get the maximum value of each group
select column_a,
max(column_b) as column_b
from your_table
group by column_a
SELECT Column_A, MAX(Column_B) FROM table
GROUP BY Column_A
You're looking for a Group By clause. Your syntax should look similar to this:
SELECT Column_A, MAX(Column_B)
FROM Table
GROUP BY Column_A
If you want to get all the columns in the table, then you have a different problem (and one not in the original posting). One reason you should add code into such a question is so you get a broader range of answers. I, for one, ignored the question, thinking it was just a newbie asking about obvious SQL functionality.
In MySQL the best approach is to use not exists:
select t.*
from table t
where not exists (select 1
from table t2
where t2.column_a = t.column_a and
t2.column_b > t.column_b
);
For optimal performance, you want an index on table(column_a, column_b). Also, this can return multiple rows, if there are duplicated maximum values.
This query is not intuitive. What it is doing is: "Get me all rows from the table where there is no other row with the same column_a value and a higher column_b value". If you think about it, this is the same as getting the maximum value. This has better performance than other methods (notably, aggregation and join), because MySQL does a simple index lookup for each row in the table. That is faster than aggregation and join.
select * from (select * from yourtable order by column_b desc)t group by column_a

Join Left or WHERE solution - Most efficient?

I am learning about databases at college, and have the assignment about finding the minimum avg exam grade for a college course. I have made two solutions, but I hope you experts in here can help me with:
What is the best/most effective solution?
Solution 1:
SELECT courses.name , MIN(avg_grade)
FROM (SELECT courseCode, AVG(grade) as avg_grade
FROM exams
GROUP BY courseCode) avg_grades, courses
WHERE courses.code = avg_grades.courseCode
Solution 2:
SELECT name, min(avg_grade)
FROM (SELECT courses.name, AVG(grade) as avg_grade
FROM courses
LEFT JOIN exams on exams.courseCode = courses.code
GROUP BY courseCode) mytable
And I have been thinking about if JOIN or LEFT JOIN is the correct to use here?
Your two queries are different, so you can't really compare efficiency, your second query will return records for courses with no exam results.
Assuming that you switch the LEFT JOIN to an INNER to make the queries comparable, then I would expect the first query to be slightly more efficient since it only has one
derived table, and the second has two:
Solution 1:
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS KEY KEY_LEN REF ROWS FILTERED EXTRA
1 PRIMARY ALL 5 100
1 PRIMARY courses ALL 5 100 Using where; Using join buffer
2 DERIVED exams ALL 5 100 Using temporary; Using filesort
Solution 2:
ID SELECT_TYPE TABLE TYPE POSSIBLE_KEYS KEY KEY_LEN REF ROWS FILTERED EXTRA
1 PRIMARY ALL 5 100
2 DERIVED courses ALL 5 100 Using temporary; Using filesort
2 DERIVED exams ALL 5 100 Using where; Using join buffer
I would however check this against your own execution plans as mine was just a quick example on SQL Fiddle.
I would like to take this chance to advise against using the ANSI-89 implicit join syntax, it was replaced over 20 years ago by the explicit join syntax in the ANSI-92 standard. Aaron Bertrand has written a great article on why to switch, I won't duplicate it here.
Another, much more important point though is that your queries are not deterministic, that is to say you could run the same query twice and get 2 different results even with no underlying change in the data.
Taking your second query as an example (although you will notice both queries are wrong on the SQL-Fiddle), you have a subquery MyTable like so:
SELECT courses.name, AVG(grade) as avg_grade
FROM courses
LEFT JOIN exams on exams.courseCode = courses.code
GROUP BY courseCode
This returned a table like so:
Name | avg_grade
--------+--------------
A | 10
B | 5
C | 6
D | 7
E | 2
You may expect the query as a whole to return:
Name | avg_grade
--------+--------------
E | 2
Since 2 is the lowest average grade, and E is the name that corresponds with that. You would be wrong though, as demonstrated here you can see this actually returns:
Name | avg_grade
--------+--------------
A | 2
What is essentially happening is that MySQL is calculating the minimum avg_grade correctly, but since you have not added any columns to the group by you have given MySQL Carte blanche to choose any value for Name it chooses.
To get the output you want, I think you need:
SELECT courses.name , MIN(avg_grade)
FROM ( SELECT courseCode, AVG(grade) as avg_grade
FROM exams
GROUP BY courseCode
) avg_grades
INNER JOIN courses
ON courses.code = avg_grades.courseCode
GROUP BY courses.Name;
Or if you only want to the course with the lowest average grade then use:
SELECT courseCode, AVG(grade) as avg_grade
FROM exams
GROUP BY courseCode
ORDER BY avg_grade
LIMIT 1;
Examples on SQL Fiddle
Please excuse the laziness of what I am about to do, but I have explained this problem a lot before, and now have a standard response that I post to explain the issue of MySQL grouping. It goes into more detail than the above, and hopefully explains it further.
MySQL Implicit Grouping
I would advise to avoid the implicit grouping offered by MySQL where possible, by this i mean including columns in the select list, even though they are not contained in an aggregate function or the group by clause.
Imagine the following simple table (T):
ID | Column1 | Column2 |
----|---------+----------|
1 | A | X |
2 | A | Y |
In MySQL you can write
SELECT ID, Column1, Column2
FROM T
GROUP BY Column1;
This actually breaks the SQL Standard, but it works in MySQL, however the trouble is it is non-deterministic, the result:
ID | Column1 | Column2 |
----|---------+----------|
1 | A | X |
Is no more or less correct than
ID | Column1 | Column2 |
----|---------+----------|
2 | A | Y |
So what you are saying is give me one row for each distinct value of Column1, which both results sets satisfy, so how do you know which one you will get? Well you don't, it seems to be a fairly popular misconception that you can add and ORDER BY clause to influence the results, so for example the following query:
SELECT ID, Column1, Column2
FROM T
GROUP BY Column1
ORDER BY ID DESC;
Would ensure that you get the following result:
ID | Column1 | Column2 |
----|---------+----------|
2 | A | Y |
because of the ORDER BY ID DESC, however this is not true (as demonstrated here).
The MySQL documents state:
The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause.
So even though you have an order by this does not apply until after one row per group has been selected, and this one row is non-deterministic.
The SQL-Standard does allow columns in the select list not contained in the GROUP BY or an aggregate function, however these columns must be functionally dependent on a column in the GROUP BY. For example, ID in the sample table is the PRIMARY KEY, so we know it is unique in the table, so the following query conforms to the SQL standard and would run in MySQL and fail in many DBMS currently (At the time of writing Postgresql is the closest DBMS I know of to correctly implementing the standard):
SELECT ID, Column1, Column2
FROM T
GROUP BY ID;
Since ID is unique for each row, there can only be one value of Column1 for each ID, one value of Column2 there is no ambiguity about what to return for each row.
EDIT
From the SQL-2003-Standard (5WD-02-Foundation-2003-09 - page 346) - http://www.wiscorp.com/sql_2003_standard.zip
If T is a grouped table, then let G be the set of grouping columns of T. In each contained
in , each column reference that references a column of T shall reference some column C that
is functionally dependent on G or shall be contained in an aggregated argument of a
whose aggregation query is QS.

Mode calculation without a subquery field in MySQL?

In my application, each product group has many products, and each product has one manufacturer. These relations are stored by MySQL in InnoDB tables product_groups with an id field, and products with id, product_group and manufacturer fields.
Is there a way to find the most common manufacturer in each product group, without resorting to selecting subqueries?
This is how I'm doing it currently:
SELECT product_groups.id,
(
SELECT manufacturer FROM products
WHERE product_group = product_groups.id
GROUP BY manufacturer
ORDER BY count(*) DESC
LIMIT 1
) manufacturer_mode
FROM product_groups;
Try this solution:
SELECT
a.product_group,
SUBSTRING_INDEX(GROUP_CONCAT(a.manufacturer ORDER BY a.occurrences DESC SEPARATOR ':::'), ':::', 1) AS manufacturer_mode
FROM
(
SELECT
aa.product_group,
aa.manufacturer,
COUNT(*) AS occurrences
FROM
products aa
GROUP BY
aa.product_group,
aa.manufacturer
) a
GROUP BY
a.product_group
Explanation:
This still uses a form of subquery, but one which executes only once as opposed to one that executes on a row-by-row basis such as in your original example.
It works by first selecting the product_group id, the manufacturer, and the count of how many times the manufacturer appears for each particular group.
The FROM sub-select will look something like this after execution (just making up data here):
product_group | manufacturer | occurrences
---------------------------------------------------
1 | XYZ | 4
1 | Test | 2
1 | Singleton | 1
2 | Eloran | 2
2 | XYZ | 1
Now that we have the sub-select result, we need to pick out the row that has the maximum in the occurences field for each product group.
In the outer query, we group the subselect once again by the product_group field, but this time, only the product_group field. Now when we do our GROUP BY here, we can use a really compelling function in MySQL called GROUP_CONCAT which we can use to concatenate the manufacturers together and in any order we want.
...GROUP_CONCAT(a.manufacturer ORDER BY a.occurrences DESC SEPARATOR ':::'...
What we are doing here is concatenating the manufacturers together that are grouped together per product_group id, the ORDER BY a.occurrences DESC makes sure that the manufacturer with the most appearances appears first in the concatenated list. Finally we are separating each manufacturer with :::. The result of this for product_group 1 will look like:
XYZ:::Test:::Singleton
XYZ appears first since it has the highest value in the occurance field. We only want to select XYZ, so we encase the concatenation within SUBSTRING_INDEX, which will allow us to only pick the first element of the list based on the ::: delimiter.
The end result will be:
product_group | manufacturer_mode
---------------------------------------
1 | XYZ
2 | Eloran

getting individual records from a group by

I have two tables, one is a table of names with a category tag and the other is a table of scores for each name
ID Name Category
1 Dave 1
2 John 1
3 Lisa 2
4 Jim 2
and the score table is
PersonID Score
1 50
2 100
3 75
4 50
4 75
I would then like a query that returned something like
Category TotalScore Names
1 150 Dave, John
2 200 Lisa, Jim
Is this possible to do with one query?
I can get the totals with a sum query and grouping by category but cannot see a way to get the names as I would like.
Many thanks
You need to use group_concat:
select Category, sum(Score) as TotalScore, group_concat(Name) as Names from categories
join scores on scores.category = categories.category
group by category
Or even better:
group_concat(DISTINCT Name ORDER BY Name ASC SEPARATOR ',') as names
Just add group_concat(Name) as names into your sum query.
Here is a solution working for Postgres (which doesn't have a group_concat() function):
select category, sum(score) as TotalScore, array(select id from perso where category=P.category order by id) as Names from perso P JOIN scores S ON S."PersonID" = P.id GROUP BY category;
(I know this was a MySQL question, but nonetheless someone might google it up but needs an answer for Postgres :) )