construct Mysql Query - mysql

Suppose I have a table called tblSchoolSupplies that looks like this:
itemsID categoryID subCategoryID itemName
1 1 1 pencil
2 1 1 eraser
3 2 2 toilet paper
4 1 2 bond paper
5 1 3 bag
6 1 1 ruler
7 1 2 compass
8 1 3 pencil case
9 2 2 soap
What I want to do is construct a query that meets these 4 criteria:
1) select rows under categoryID = 1
2) group rows by subCategoryID
3) limit 2 rows per subCategoryID
4) rows must be selected by random

Doug R's comment should be taken to heart. Please always include what you have tried. The questions you have are of varying difficulty and I feel like an answer will help you and others.
Example table and queries are here: http://sqlfiddle.com/#!9/3beee/6
In order to select records with category of 1, use the query below. The WHERE clause helps filter your records to only category 1
select * from tblSchoolSupplies where categoryID = 1;
Grouping rows by sub category requires more information. You'd generally group information to get statistics. For example, how many items are there in each subcategory or how many categories do each sub-category belong. Notice that I am selecting subCategoryID and doing GROUP BY on it also. Other columns are statistical calculations. Most, if not all, GROUP BY queries you will encounter will have a dimension like subCategoryID that is grouped along with statistical functions like sum, count, avg etc.
select
subCategoryID,
count(*) as items_in_subcategory,
count(distinct categoryID) as distinct_categories
from tblSchoolSupplies
group by subCategoryID;
Limiting 2 rows per subCategoryID is more challenging in comparison to your first question. The answer below is based on question 12113699
-- limit 2 rows per subCategoryID
set #number := 0;
set #subCategoryID := '';
select *
from
(
select *,
#number:=if(#subCategoryID = subCategoryID, #number + 1, 1) as rownum,
#subCategoryID:=subCategoryID as field1
from tblSchoolSupplies
order by subCategoryID, itemsID
) as subcat
where subcat.rownum < 3;
Using a random sort order and limiting only 1 record output will give you a randomly selected row. Please read through discussion in question 4329396 to gain different perspective on similar question(s).
select * from tblSchoolSupplies order by rand() limit 1;

Related

How to GROUP BY 2 different columns together

I have 2 columns having users id participating in a transaction, source_id and destination_id. I'm building a function to sum all transactions grouped by any user participating on it, either as source or as destination.
The problem is, when I do:
select count (*) from transactions group by source_id, destination_id
it will first group by source, then by destination, I want to group them together. Is it possible using only SQL?
Sample Data
source_user_id destination_user_id
1 4
3 4
4 1
3 2
Desired result:
Id Count
4 - 3 (4 appears 3 times in any of the columns)
3 - 2 (3 appears 2 times in any of the columns)
1 - 2 (1 appear 2 times in any of the columns)
2 - 1 (1 appear 1 time in any of the columns)
As you can see on the example result, I want to know the number of times an id will appear in any of the 2 fields.
Use union all to get the id's into one column and get the counts.
select id,count(*)
from (select source_id as id from tbl
union all
select destination_id from tbl
) t
group by id
order by count(*) desc,id
edited to add: Thank you for clarifying your question. The following isn't what you need.
Sounds like you want to use the concatenate function.
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_concat
GROUP BY CONCAT(source_id,"_",destination_id)
The underscore is intended to distinguish "source_id=1, destination_id=11" from "source_id=11, destination_id=1". (We want them to be 1_11 and 11_1 respectively.) If you expect these IDs to contain underscores, you'd have to handle this differently, but I assume they're integers.
It may look like this.
Select id, count(total ) from
(select source_id as id, count (destination_user_id) as total from transactions group by source_id
union
select destination_user_id as id , count (source_id) as total from transactions group by destination_user_id ) q group by id

Mysql group by two columns and pick the maximum value of third column

I have a table that has user_id, item_id and interaction_type as columns. interaction_type could be 0, 1,2,3,4 or 5. However, for some user_id and item_id pairs, we might have multiple interaction_types. For example, we might have:
user_id item_id interaction_type
2 3 1
2 3 0
2 3 5
4 1 0
5 4 4
5 4 2
What I want is to only keep the maximum interaction_type if there are multiples. So I want this:
user_id item_id interaction_type
2 3 5
4 1 0
5 4 4
Here is the query I wrote for this purpose:
select user_id, item_id, max(interaction_type) as max_type
from mytable
group by user_id, item_id;
But the result is weird. For example, in the original table I have 100000 rows with interaction_type=5 but in the result table I have only 2000. How is this possible as the max will pick 5 between every comparison that contains 5 and therefore I shouldn't have fewer 5 in the result table.
Your query is fine. The reason you are getting 2000 rows is because you are getting one row for every unique pair of values user_id, item_id.
If you want to see the interaction types going into each row then use:
select user_id, item_id, max(interaction_type) as max_type,
group_concat(distinct interaction_type) as interaction_types,
count(*) as cnt
from mytable
group by user_id, item_id;
It occurs to me that you want all rows with the maximum interaction type. If so, calculate the maximum and then find all rows that match that value:
select t.*
from mytable t cross join
(select max(interaction_type) as maxit from mytable) x
on x.maxit = t.interaction_type;
No group by is needed for this query.

Adding Row Values when there are no results - MySQL

Problem Statement: I need my result set to include records that would not naturally return because they are NULL.
I'm going to put some simplified code here since my code seems to be too long.
Table Scores has Company_type, Company, Score, Project_ID
Select Score, Count(Project_ID)
FROM Scores
WHERE company_type= :company_type
GROUP BY Score
Results in the following:
Score Projects
5 95
4 94
3 215
2 51
1 155
Everything is working fine until I apply a condition to company_type that does not include results in one of the 5 score categories. When this happens, I don't have 5 rows in my result set any more.
It displays like this:
Score Projects
5 5
3 6
1 3
I'd like it to display like this:
Score Projects
5 5
4 0
3 6
2 0
1 3
I need the results to always display 5 rows. (Scores = 1-5)
I tried one of the approaches below by Spencer7593. My simplified query now looks like this:
SELECT i.score AS Score, IFNULL(count(*), 0) AS Projects
FROM (SELECT 5 AS score
UNION ALL
SELECT 4
UNION ALL
SELECT 3
UNION ALL
SELECT 2
UNION ALL
SELECT 1) i
LEFT JOIN Scores ON Scores.score = i.score
GROUP BY Score
ORDER BY i.score DESC
And gives the following results, which is accurate except that the rows with 1 in Projects should actually be 0 because they are derived by the "i". There are no projects with a score of 5 or 2.
Score Projects
5 1
4 5
3 6
2 1
1 3
Solved! I just needed to adjust my count to specifically look at the project count - count(project) rather than count(*). This returned the expected results.
If you always want your query to return 5 rows, with Score values of 5,4,3,2,1... you'll need a rowsource that supplies those Score values.
One approach would be to use a simple query to return those fixed values, e.g.
SELECT 5 AS score
UNION ALL SELECT 4
UNION ALL SELECT 3
UNION ALL SELECT 2
UNION ALL SELECT 1
Then use that query as inline view, and do an outer join operation to the results from your current query
SELECT i.score AS `Score`
, IFNULL(q.projects,0) AS `Projects`
FROM ( SELECT 5 AS score
UNION ALL SELECT 4
UNION ALL SELECT 3
UNION ALL SELECT 2
UNION ALL SELECT 1
) i
LEFT
JOIN (
-- the current query with "missing" Score rows goes here
-- for completeness of this example, without the query
-- we emulate that result with a different query
SELECT 5 AS score, 95 AS projects
UNION ALL SELECT 3, 215
UNION ALL SELECT 1, 155
) q
ON q.score = i.score
ORDER BY i.score DESC
It doesn't have to be the view query in this example. But there does need to be a rowsource that the rows can be returned from. You could, for example, have a simple table that contains those five rows, with those five score values.
This is just an example approach for the general approach. It might be possible to modify your existing query to return the rows you want. But without seeing the query, the schema, and example data, we can't tell.
FOLLOWUP
Based on the edit to the question, showing an example of the current query.
If we are guaranteed that the five values of Score will always appear in the Scores table, we could do conditional aggregation, writing a query like this:
SELECT s.score
, COUNT(IF(s.company_type = :company_type,s.project_id,NULL)) AS projects
FROM Scores s
GROUP BY s.score
ORDER BY s.score DESC
Note that this will require a scan of all the rows, so it may not perform as well. The "trick" is the IF function, which returns a NULL value in place of project_id, when the row would have been excluded by the WHERE clause.)
If we are guaranteed that project_id is non-NULL, we could use a more terse MySQL shorthand expression to achieve an equivalent result...
, IFNULL(SUM(s.company_type = :company_type),0) AS projects
This works because MySQL returns 1 when the comparison is TRUE, and otherwisee returns 0 or NULL.
Try something like this:
select distinct score
from (
select distinct score from scores
) s
left outer join (
Select Score, Count(Project_ID) cnt
FROM Scores
WHERE company_type= :company_type
) x
on s.score = x.score
Your posted query would not work without a group by statement. However, even there, if you don't have those particular scores for that company type, it wouldn't work either.
One option is to use an outer join. That would require a little more work though.
Here's another option using conditional aggregation:
select Score, sum(company_type=:company_type)
from Scores
group by Score

How to get the item that has the highest number of occurence in a mysql table

I have a table of products as shown below:
id name quantity
1 shoe 2
2 pen 1
3 shoe 1
4 glass 3
5 pen 4
6 shoe 2
I want to get the item that occurs more in the table and the number of rows it occupies or the how many times it is repeated in the table.
In the case of the above table, shoe occurs the highest number of times i.e. 3 times. I need the mysql query that can permit me to do this (return 3 in the above case).
Please take performance into consideration, since this query will be perform over a table having about 10 million records. Thank you!
SELECT name,count(*) FROM products GROUP BY name ORDER BY count(*) DESC limit 1
This may work
A basic GROUP BY will do:
select top 1 name, count(name), sum(quantity)
from XX
group by name
order by count(name) desc

selecting the next m rows after row number n in a mysql table

In a mysql table with "R rows" I want to select the next "m rows" after "nth row" in such a way that if n+m>R it returns R-n rows from the end of table and m+n-R rows from the beginning of the table.
e.g in this table:
id firstname
1 john
2 robert
3 bob
4 adam
5 david
I want to get the next 4 rows after row number 3 (bob), in this fashion:
4 adam
5 david
1 john
2 robert
I have searched a lot and found that the following query just returns the last 2 rows.
SELECT * FROM table LIMIT 4 OFFSET 3;
I know that I can implement this specific query using php and bunch of conditional statements but I am curious to know whether it has been implemented in mysql or not?
One approach is to use union all in a subquery. This allows you to "duplicate" the table, with a newly calculated id at the end of the table:
select t.*
from ((select t.*, id as newid from table t) union all
(select t.*, id + cnt as newid
from table t cross join
(select count(*) as cnt from table) cnt
)
) t
order by newid
limit 4 offset 3;
For small tables, this should be fine. For larger tables, you probably don't want to do this because the MySQL materializes the subquery -- adding overhead to the processing of the query.