Subqueries with Like, count, group by - mysql

My table:
+----+------------+-------+-------+
| id | Name | Type | Code |
+----+------------+-------+-------+
| 1 | /Color | black | cod-1 |
| 2 | Colorless | black | cod-2 |
| 3 | Colorful | black | cod-1 |
| 4 | Color/ | black | cod-3 |
| 5 | Colored | blue | cod-1 |
| 6 | Bottle | black | cod-1 |
| 7 | Bottles | black | cod-2 |
| 8 | Bottle/z | black | cod-1 |
| 9 | Bottleneck | blue | cod-1 |
+----+------------+-------+-------+
For a selected Type, I need to group by (like) Names and count by distinct code.
In the end, the final count must give:
Type Name like Count Code
Black Color 3
Black Bottle 2
Blue Color 1
Blue Bottle 1
... and so on for unpredictable and randomized thousands of names containing a similar sequence of characters.
So far, any attempt was unsuccessfully. Any clue ?

To accomplish what you want I think you either need a lookup table from which you can match the names (a table holding synonyms), or you could hard code the logic like this:
SELECT
type AS "Type",
CASE
WHEN name LIKE '%Color%' THEN 'Color'
WHEN name LIKE '%Bottle%' THEN 'Bottle'
END AS "Name",
COUNT(DISTINCT code) AS "Count Code"
FROM your_table
GROUP BY
type,
CASE
WHEN name LIKE '%Color%' THEN 'Color'
WHEN name LIKE '%Bottle%' THEN 'Bottle'
END
ORDER BY type
This would give you the result you want, as seen in this SQL Fiddle. This is obviously not a good solution - using a lookup table would be a lot better.
Using a lookup table could be done like this:
create table lookup (k varchar(20), v varchar(30));
insert into lookup values
('Bottle','Bottle'),('Bottle','Bottles'),
('Bottle','Bottle/z'),('Bottle','Bottleneck'),
('Color','Color'),('Color','Colorless'),
('Color','Colorful'),('Color','/Color'),
('Color','Color/'),('Color','Colored');
SELECT
type AS "Type",
l.k AS "Name",
COUNT(DISTINCT code) AS "Count Code"
FROM your_table tbl
INNER JOIN lookup l on tbl.name = l.v
GROUP BY type, l.k
Sample SQL Fiddle using lookup table.

Try this.
Select distinct Type, Name, count(code) from MyTable
group by Type, Name

Related

How to calculate count of each value in MySQL JSON array?

I have a MySQL table with the following definition:
mysql> desc person;
+--------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | text | YES | | NULL | |
| fruits | json | YES | | NULL | |
+--------+---------+------+-----+---------+-------+
The table has some sample data as follows:
mysql> select * from person;
+----+------+----------------------------------+
| id | name | fruits |
+----+------+----------------------------------+
| 1 | Tom | ["apple", "orange"] |
| 2 | John | ["apple", "mango"] |
| 3 | Tony | ["apple", "mango", "strawberry"] |
+----+------+----------------------------------+
How can I calculate the total number of occurrences for each fruit? For example:
+------------+-------+
| fruit | count |
+------------+-------+
| apple | 3 |
| orange | 1 |
| mango | 2 |
| strawberry | 1 |
+------------+-------+
Some research shows that the JSON_LENGTH function can be used but I cannot find an example similar to my scenario.
You can use JSON_EXTRACT() function to extract each value ("apple", "mango", "strawberry" and "orange") of all three components of the arrays, and then then apply UNION ALL to combine all such queries:
SELECT comp, count(*)
FROM
(
SELECT JSON_EXTRACT(fruit, '$[0]') as comp FROM person UNION ALL
SELECT JSON_EXTRACT(fruit, '$[1]') as comp FROM person UNION ALL
SELECT JSON_EXTRACT(fruit, '$[2]') as comp FROM person
) q
WHERE comp is not null
GROUP BY comp
Indeed If your DB's version is 8, then you can also use JSON_TABLE() function :
SELECT j.fruit, count(*)
FROM person p
JOIN JSON_TABLE(
p.fruits,
'$[*]' columns (fruit varchar(50) path '$')
) j
GROUP BY j.fruit;
Demo
You can't do it without first creating a table with one row per fruit.
CREATE TABLE allfruits (fruit VARCHAR(10) PRIMARY KEY);
INSERT INTO allfruits VALUES ('apple'), ('orange'), ('mango'), ('strawberry');
There is not a good way to generate this from the JSON.
Once you have that table, you can join it to the JSON and then use GROUP BY to count the occurrences.
SELECT fruit, COUNT(*) AS count
FROM allfruits
JOIN person ON JSON_SEARCH(person.fruits, 'one', fruit) IS NOT NULL
GROUP BY fruit;
Output:
+------------+-------+
| fruit | count |
+------------+-------+
| apple | 3 |
| mango | 2 |
| orange | 1 |
| strawberry | 1 |
+------------+-------+
Note that it will do a table-scan on the person table to find each fruit. This is pretty inefficient, and as your person table gets larger, it will become a performance problem.
If you want to optimize for this type of query, then you shouldn't use JSON to store an array of fruits. You should store data in a normalized way, representing the many-to-many relationship between persons and fruits with another table.
This is related to my answer to Is storing a delimited list in a database column really that bad?
I think the simplest solution would be to use JSON_TABLE function.
The query you need is
select ft.fruit, count(ft.fruit) from person,
json_table(
fruits,
'$[*]' columns(
fruit varchar(128) path '$'
)
) as ft
group by ft.fruit
;
You can find working example in this dbfiddle
Fruit demo

Union in same table with ACCESS

I have a table with this sort of data:
+------------+----------+----------+
| Unique ID | Name | Class |
+------------+----------+----------+
| 1 | Name 1 | Class A |
| 2 | Name 2 | "" |
| 3 | Name 3 | Class C |
| 4 | Name 1 | "" |
| 5 | Name 4 | "" |
| 6 | Name 4 | "" |
+------------+----------+----------+
I am trying to do something I thought was simple, but i did not find so.
I am trying to "extract" only the lines with an empty string value in 'Class' for a group of equal names.
So in this case I would get this result :
+------------+----------+--------+
| Unique ID | Name | Class |
+------------+----------+--------+
| 2 | Name 2 | "" |
| 5 | Name 4 | "" |
+------------+----------+--------+
So not Name 1 because even though there is a line with "" there is another line with 'Class A'.
I thought a UNION would do the job but I am not gettgin anything because I think unions are for two tables but the problem here is I have the data in the same table.
Thank you for your help
Access syntax may be a bit different but this returns what you want in Oracle:
SELECT distinct Name, Class FROM table1 Where Name NOT in (select name from table1 where class is not null)
A Union melds two result sets, whether or not they come from the same table is irrelevant. What you want to do is omit from the result set the rows with the same name AND class is not null. Not having your query to expand on or change is a problem, but if you add a clause that says something like where "name not in (select name from table where class is not null);", that may do it.

Flag the row with the max value in field B based on name field in Field A

I am able to find the row with the maximum value for a Value field in an given set of records with the same name using
Select Name, Max(Value) from table group by Name, Value
which returns to me the record with the highest value but I am looking to turn this into an update so I can
Flag the record with the highest value in a IsMaxValue
For each record in the Name, Value group store the highest value found in a 'MaxValue' field
Simple select version is here:
http://sqlfiddle.com/#!9/ccd32/5
with fields ready for updates as per above if it is possible.
I believe this statement is what you might be looking for:
update maxvalues
join (
Select Color, Max(`Value`) max_value
from MaxValues
group by Color
) a on maxvalues.color = a.color and value = a.max_value
set ismaxrecord = '1', maxrecordid = a.max_value;
Sample SQL Fiddle
Given your sample data the table would look like below after the update:
| Color | Value | IsMaxRecord | MaxRecordID |
|--------|-------|-------------|-------------|
| Orange | 1 | | 0 |
| Orange | 2 | | 0 |
| Orange | 3 | 1 | 3 |
| Black | 30 | 1 | 30 |
| Black | 20 | | 0 |
| Black | 10 | | 0 |

implementing Some kind of pivot in mysql

I have a table in mysql in this structure
table: member
| Id | Name | Lastname | Username
| --------------------------------
| 1 | Alexi| Lalas | alexi
| 2 | Jack | Louis | louis
And I have a table called member images with this structre:
table: image
| Id | MemberId | Image | Type |
|------------------------------------|
| 50 | 1 | face.jpg |Avetar |
| 51 | 1 | image.jpg |Gallery|
| 52 | 2 | main.jpg |Avetar |
| 53 | 2 | jungle.jpg |Gallery|
And I want to get this result
| Id | Name | Lastname | Username | Image1 | Image2 |
|-------------------------------------------------------|
| 1 | Alexi| Lalas | alexi |face.jpg |image.jpg |
| 2 | Jack | Louis | louis |main.jpg |jungle.jpg|
Becuase of some reasons I can't handle is on app side and I have to do it on sql side.
Imagin that I always have 2 type of images and we always have Image1 and Image2.
Any help would be appritiated.
Since they are only two types, you can use the CASE expression to do so. Something like this:
SELECT
m.Id,
m.Name,
m.LastName,
m.UserName,
MAX(CASE WHEN i.Type = 'Avetar' THEN i.Image END) AS 'Image1',
MAX(CASE WHEN i.Type ='Gallery' THEN i.Image END) AS 'Image2'
FROM member AS m
LEFT JOIN image AS i ON m.Id = i.MemberId
GROUP BY m.Id,
m.Name,
m.LastName,
m.UserName;
Note that: LEFT JOIN will include all the members from the member table even if they have no images in the image table, in this case NULL will be returned.
See it in action here:
SQL Fiddle Demo
The way to do this is via a subselect (subquery). You would subselect the first column as the image for the user withe Avetar type and the other column subselect would be for the image with the type Gallery. If you can't find a way to make User ID and Type unique in your sub table then you are going to have issues with this type of query. Keep in mind that a subselects can impact performance heavily.
http://dev.mysql.com/doc/refman/5.0/en/subqueries.html

Mysql group by in a CSV field

What's the best way to perform a group by statement in a csv-like column using SQL (mySQL)?
Table Products Color
+-------------+--------------+
| Product Id | Color |
+-------------+--------------+
| 120 | Red, Blue |
| 121 | Green, Black |
| 122 | Red, Black |
+-------------+--------------+
From the table above I need to count how many times a color appears and return something like this:
+-------------+-------+
| Color | Count |
+-------------+-------+
| Red | 2 |
| Blue | 1 |
| Green | 1 |
| Black | 2 |
+-------------+-------+
Is possible to do this without normalize the database?
If you can't change your table structure, but you can create a table that lists all the colors, you could use this query:
SELECT Colors.Color, Count(*)
FROM
Products INNER JOIN Colors
ON FIND_IN_SET(Colors.Color, REPLACE(Products.Color, ' ', ''))>0
GROUP BY
Colors.Color
See it working here. Please notice that it can't be optimized because it can't make use on an index.