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.
Related
I have two databases, one database containing all my pictures like:
Database 1 name: images
-----+----------------------------+----------------------+-----------+------------+------------
| id | description | tags | imagename | resolution | location |
-----+----------------------------+----------------------+-----------+------------+------------
| 1 | We standing at eiffeltower | france, green, blue | IMG01.JPG | 1280x1024 | /img/2020 |
| 2 | We standing at bridge | france, orange, grey | IMG02.JPG | 1280x1024 | /img/2020 |
Database 2 name tagTranslations (for Dutch translation)
-----+--------+-----------------------
| id | tag | translation |
-----+--------+-----------------------
| 1 | france | frankrijk |
| 2 | orange | oranje. |
| 3 | grey | grijs. |
| 4 | green | groen |
| 5 | blue | blauw |
Now i want with 1 mysql query to get a result like this:
"We standing at eiffeltower", "france, green, blue", "IMG01.JPG", "1280x1024", "/img/2020", "frankrijk", "groen", "blauw"
"We standing at bridge", "france, orange, grey", "IMG02.JPG", "1280x1024", "/img/2020", "frankrijk", "oranje", "grijs"
You first effort should go into fixing your data modeL Each image tag should be stored in a separate table, on a different row. Storing delimited lists in database columns in the root of many evils, as you are starting to see. More about this can be read in this famous SO post.
That said, you could use a corelated subquery with find_in_set() and group_concat():
select
i.id,
i.description,
(
select group_concat(
tt.translation
order by find_in_set(tt.tag, replace(i.tags, ', ', ','))
separator ', '
)
from tagTranslations tt
where find_in_set(tt.tag, replace(i.tags, ', ', ','))
) tags,
i.imagename,
i.resolution,
i.location
from images i
The correlated subquery retrieves rows from the translation table whose tag can be found in the tags list of the corresponding images row. For this, we use handly MySQL function find_in_set() (we need to remove the space after the comma for the function to work properly); then, aggregation function group_concat() regenerates a delimited list of translations, using find_in_set() again to honor the original order of tags.
Demo on DB Fiddle:
id | description | tags | imagename | resolution | location
-: | :------------------------- | :----------------------- | :-------- | :--------- | :--------
1 | We standing at eiffeltower | frankrijk, groen, blauw | IMG01.JPG | 1280x1024 | /img/2020
2 | We standing at bridge | frankrijk, oranje, grijs | IMG02.JPG | 1280x1024 | /img/2020
Try out the code below:
CREATE VIEW table_comb AS
SELECT * FROM images
UNION ALL
SELECT * FROM tagTranslations
Imagine I've got a table that records transactions in a clothing store. Each item comes in different variety (like different colors). I want to write a query that will return a count of the number of each item sold, but ALSO tell me how many different colors have been sold of each item.
So if I've got a table like this:
+------------+
|transactions|
+------+-------+
| item | color |
+------+-------+
|shirt | red |
|shirt | red |
|shirt | blue |
|shirt | green |
|hat | blue |
|hat | red |
|pants | blue |
|pants | blue |
|pants | blue |
+------+-------+
I would want a result that looked something like this:
+------+-------+--------+
| item | count | colors |
+------+-------+--------+
|shirt | 4 | 3 |
|hat | 2 | 2 |
|pants | 3 | 1 |
+------+-------+--------+
Getting the count of each item sold is easy enough. I know I can use a query like:
SELECT item, COUNT(*) as count FROM transactions GROUP BY item
The part I'm not sure about is how to also get a count of the number of different colors of each item.
You are looking for count(distinct):
SELECT item, COUNT(*) as num, COUNT(DISTINCT color) as num_colors
FROM transactions
GROUP BY item;
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
With php I am trying to get data from 2 different tables :
Table : products
+-------+---------+----------+
| pcode | product | category |
+-------+---------+----------+
| 1 | jeans | men |
+-------+---------+----------+
| 2 | shirt | men |
+-------+---------+----------+
Table : colors
+-------+---------+
| pcode | colors |
+-------+---------+
| 1 | blue |
+-------+---------+
| 1 | black |
+-------+---------+
| 1 | white |
+-------+---------+
| 2 | yellow |
+-------+---------+
Result I want should be like :
+-------+---------+----------+---------------------+
| pcode | product | category | colors |
+-------+---------+----------+---------------------+
| 1 | jeans | men | black, blue, white |
+-------+---------+----------+---------------------+
| 2 | shirt | men | yellow |
+-------+---------+----------+---------------------+
Please guide me.
Thanks in advance.
Vikram
SELECT p.pcode,product,category,GROUP_CONCAT(colors) as colors
FROM products p JOIN colors c
ON p.pcode=c.pcode
GROUP BY product
Mihai's Query also working..
Can also try this..
select p.*, group_concat(colors separator ',') colors from products p left outer join colors c on p.pcode=c.pcode group by p.pcode;
NOTE:You could try to use PDO or MySqli libary(links bellow) to run this SQL Statement
You could try this SQL statement i think it will do the job but will show the rows in a different way that you requested just give it a try:
SELECT colors.pcode , products.products ,products.category , colors.colors
FROM colors
INNER JOIN FIRST ON colors.pcode = products.pcode
Results:
Or if you wanted like you asked you should seperate the work in multiple SQL Satatement:
SELECT colors FROM colors WHERE pcode = 1
And with some SQL Sattement get all the colors
Then get the products and the productsand categorys this way:
SELECT product , category FROM products WHERE pcode = 1
And also with some like this you will get all what you need
But i would prefer the first one as it is more handy and faster.
Links:
PDO:
http://php.net/manual/en/book.pdo.php
MYSQLI:
http://www.php.net/mysqli
Have a nice day :)
Sorry I couldn't think of a better way to title this. In Amazon's SimpleDB, an item can have multiple values in the same column, so it's possible to select only those items that have all of the attributes being sought.
In MySQL, let's say the following table ("Photo_Attributes") contains an unlimited number of attributes for photographs that are contained in another table ("Photos"), and that the two tables are joined by Item_Number.
And, let's say I wanted to find a hat whose color was red and size was medium, which in this case would be ITEM_ID "ABC" and not "OPQ".
+-----+----------+--------+-----------+-------+
| ID | Item_ID | Object | Attribute | Value |
+-----+----------+--------+-----------+-------+
| 1 | ABC | Hat | Color | Red |
+-----+----------+--------+-----------+-------+
| 2 | FGH | Pants | Color | Blue |
+-----+----------+--------+-----------+-------+
| 3 | FGH | Pants | Size | Large |
+-----+----------+--------+-----------+-------+
| 4 | LMN | Shirt | Color | Red |
+-----+----------+--------+-----------+-------+
| 5 | ABC | Hat | Size | Med |
+-----+----------+--------+-----------+-------+
| 6 | LMN | Shirt | Size | Med |
+-----+----------+--------+-----------+-------+
| 7 | OPQ | Hat | Color | White |
+-----+----------+--------+-----------+-------+
| 8 | OPQ | Hat | Size | Med |
+-----+----------+--------+-----------+-------+
The following query would yield no results because each row contains only one Attribute and one Value.
SELECT FROM Photo_Attributes WHERE OBJECT='hat' AND (Attribute='Color" AND
Value='Red") AND (Attribute='Size' AND Value='Med');
And, this query would produce more rows than it should (i.e., all red and all medium-sized items).
SELECT FROM Photo_Attributes WHERE OBJECT='hat' AND (Attribute='Color" AND
Value='Red") OR (Attribute='Size' AND Value='Med');
What's the best way to write this -and- is there a way to do it without using JOIN in the SELECT statement? I'm wondering the latter because the query would be programmatically generated (in nodejs) and the number of Attribute-Value pairs could range from one to several. I figure I could also use nested queries, culling from recordset, but that seems equally inefficient.
SELECT pa1.Item_ID
FROM Photo_Attributes pa1
INNER JOIN Photo_Attributes pa2
ON pa1.Item_ID = pa2.Item_ID
AND pa2.Attribute = 'Size'
AND pa2.Value = 'Med'
WHERE pa1.Object = 'Hat'
AND pa1.Attribute = 'Color'
AND pa1.value = 'Red'
Assuming no overlap in your name/value pairs (e.g., You'd never have Size/Red or Color/Med), you could probably also do something like this.
SELECT pa.Item_ID
FROM Photo_Attributes pa
WHERE pa.Object = 'Hat'
AND pa.Attribute IN ('Size', 'Color')
AND pa.Value IN ('Med', 'Red')
GROUP BY pa.Item_ID
HAVING COUNT(DISTINCT Value) = 2