Mysql group concat with counting items - mysql

I have 2 tables, 'item' and 'propvalues', propvalues have property_id and value column, what im trying to get is to group propvalues as property_id, and count how many similar values they have.
i know it is similar to another question, but i was unable to figure out how to change it for myself to work. if someone is good at mysql please dont mark it as a duplicate question, but rather try to give me at least some advice. i dont understand what is going on when there is an inner select statement
Here are the tables:
'item'
id category_id
1 1
2 1
3 2
4 5
etc.
'propvalues'
id item_id property_id value
1 1 3 'blue'
2 1 3 'blue'
3 1 3 'blue'
4 1 3 'red'
5 1 5 'blue'
6 1 5 'red'
7 2 5 'red'
SELECT propvalues.property_id,
GROUP_CONCAT(propvalues.value)
FROM propvalues JOIN item ON item.id = propvalues.item_id
WHERE item.category_id = 1 || item.category_id = 2
GROUP BY propvalues.property_id
this give this result:
property_id value
3 blue,blue,blue,red
5 blue,red,red
but i need this, result, i want only the unique values,and count them:
3 blue,3|red,1
5 blue,1|red,2
any ideas ?
i found some solution here: mysql group_concat with a count inside?
but im unable to figure out how to change it for myself to work.

Derived from the refered duplicate:
SELECT property_id, GROUP_CONCAT(CONCAT(value, ',', cnt) SEPARATOR '|')
FROM (
SELECT property_id, value, COUNT(value) AS cnt
FROM propvalues
JOIN item ON item.id = propvalues.item_id
AND item.category_id IN (1, 2)
GROUP BY property_id, value
) t
GROUP BY property_id
To break it down, the inner subquery fetches a number of distinct values per property_id:
+-------------+-------+-----+
| property_id | value | cnt |
+-------------+-------+-----+
| 3 | blue | 3 |
| 3 | red | 1 |
| 5 | blue | 1 |
| 5 | red | 2 |
+-------------+-------+-----+
The outer main query further aggregates theses values by distinct property_id, concatenates value and count to a string (CONCAT(value, ',', cnt)) and then concatenates those from each row (GROUP_CONCAT(... SEPARATOR '|')).

Related

How to get the column value based on multiple rows?

I want to check rows where uid equal both 1 and 2 and if they do, return cid. In this example there will only ever be 2, but if you know a way to return the CID for more than 2, that would be great too.
How can I most easily get the value where cid = 5 when I know both uid values? (1,2).
cid | uid |
------------
5 | 1 |
5 | 2 |
6 | 1 |
6 | 3 |
7 | 1 |
7 | 4 |
For pseudo sql, I am thinking something like SELECT cid WHERE uid = 1 or uid = 2
This returns all rows where uid has a 1 or a 2. How can I limit to an OR statement and an AND?
SELECT cid WHERE uid = 1 AND uid = 2 (but in multiple rows)
Any ideas?
As far as i understand you're looking for a way to apply a condition to multiple rows, a way to do that is through agrupation functions. try this:
Select CID
from YourTable where uid IN (1,2)
group by cid
having count(uid) = 2
in this example i'm using IN instead of two OR and i'm grouping the rows by CID, and after that i'm limiting the results to those rows that match with UID equals to 1 and 2.
There are many tricky ways of achieve the same result, for example you can also do something like:
Select CID
from YourTable where uid IN (1,2)
group by cid
having sum(uid) = 3
in this example i'm suming the UID column, if UID is 1 and 2 the sum of both will result on 3, I assume that you can't have 3 rows with the UID 1 and the same CID
According to given details, Try this. Let's say you have a table called docs;
SELECT d1.cid
FROM docs AS d1
LEFT JOIN docs AS d2 ON d1.cid = d2.cid
WHERE d1.uid = 1
AND d2.uid = 2

Can I count multiple columns with Group By?

I have a table with these columns:
s, s2, s3
1, 2, 3
4
1, 3
4, 2,
2, 1
3, 4
4
I want to know how many times the unique values in column s appears in the columns s, s2 and s3.
So far I have:
$query = "SELECT s, COUNT(*) as count FROM table GROUP BY s";
This will give me:
1 - count 2
2 - count 1
3 - count 1
4 - count 3
But I want to count the column s2 and s3 also so the outcome will be:
1 - count 3
2 - count 3
3 - count 3
4 - count 4
Any idea how I must edit the query so I can count the columns s, s2 and s3 group by the values of column s?
Kind regards,
Arie
You need a UNION ALL for all the columns and then count them:
select
t.s, count(*) counter
from (
select s from tablename union all
select s2 from tablename union all
select s3 from tablename
) t
where t.s is not null
group by t.s
See the demo.
Results:
| s | counter |
| --- | ------- |
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
| 4 | 4 |
If in the columns s2 and s3 there are values that do not exist in the column s and you want them excluded, then instead of:
where t.s is not null
use
where t.s in (select s from tablename)
#forpas answer is a good one. However, two things you should consider.
Due to the use of union the query will become slower as the data size increases.
If the input is as following:
s, s2, s3
1, 2, 3
4
1, 3
4, 2,
2, 1
3, 4
4 5
The result of the provided query will be:
| s | counter |
| --- | ------- |
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
| 4 | 4 |
| 5 | 1 |
whereas it should remain the same as 5 is not present into the s column.
In order to resolve both of the above issues, I propose the approach to use JOIN instead of UNION:
SELECT t3.s, IF(t3.s = t4.s3, cnt1 + 2, cnt1 + 1) as counter FROM
(SELECT *, count(*) AS cnt1 FROM
(SELECT s from table) AS t1
LEFT JOIN
(SELECT s2 FROM table) AS t2
ON t1.s = t2.s2 GROUP BY t1.s
) AS t3
LEFT JOIN
(SELECT s3 FROM table) AS t4
ON t3.s = t4.s3
ORDER BY t3.s
The query might look a bit lengthy and complicated but it is really simple when you look into the logic.
Step 1
What I have done here is to make a left join from s column to s2 and counted results for that so it will give you 1 lesser number than how many numbers are present in total as it will make relation left to right.
Step 2
Then I have made a left join from s to s3, and only increase the count of step 1 by 1 if the relation is found.
Step 3
Ultimately I have increased the count by 1 so that we can convert the number of relations to the number of the enities.
I hope it makes sense

returning rows satisfying multiple conditions for a column from a mysql table

I have mysql table with hospitals and treatments(associated with sub treatments) that they provide. I need to make mysql query on the table which returns hospitals providing all treatment/sub_treatment given in a list. For example:
From table below I need hospitals providing all treatments in list: (tretament_id, sub_treatment_id) = (1-1, 1-2). So result must be hospitals with id 1 and 8.
hospital_id | treatment_id | sub_treatment_id
-------------------------------------------------
1 | 1 | 1
1 | 1 | 2
1 | 1 | 3
_________________________________________________
4 | 1 | 1
4 | 2 | 1
_________________________________________________
8 | 1 | 1
8 | 1 | 2
_________________________________________________
7 | 2 | 1
I tried WHERE IN but it works like OR so returns hospital 4 which satisfies only (1,1). How can I write an sql query like WHERE IN but which works like AND?
Try this:
SELECT hospital_id
FROM mytable
WHERE (treatment_id, sub_treatment_id) IN ((1, 1), (1, 2))
GROUP BY hospital_id
HAVING COUNT(CASE
WHEN (treatment_id, sub_treatment_id) IN ((1, 1), (1, 2))
THEN 1
END) = 2
Demo here
You can do this using group by and having:
select hospital_id
from t
where treatment_id = 1 and sub_treatment_id in (1, 2)
group by hospital_id
having count(*) = 2;
Note: This assumes that there are no duplicates in the table. That is easy enough to fix using count(distinct), but probably not necessary.
Here is a solution using GROUP_CONCAT and JOIN:
select distinct t.hospital_id
from hospitals h and treatments t ON h.id = t.hospital_id
having GROUP_CONCAT(CONCAT(t.treatment_id, '-', t.sub_treatment_id)
ORDER BY t.treatment_id, t.sub_treatment_id)
= '1-1,1-2';

Select one fixed line and several arbitrary

I have a table with many lines. I need select several (no more than three) lines with certain values and one more. Moreover, need ORDER BY id DESC and required line position before other. Example:
id | name | group
-----------------
1 | One | null
2 | Two | null
3 | Three| 2
4 | Four | 3
5 | Five | 1
6 | Six | 2
I need lines with group == 2 (no more than three) and line with id == 2. Result:
id | name | group
-----------------
2 | Two | null
3 | Three| 2
6 | Six | 2
Line with id == 2 must be selected, other lines - no more than three. If I use WHERE id = 2 OR group = 2 LIMIT 4 than if exist more than 4 lines with group == 2, then required line with id == 2 not selected.
How solve the problem in one SQL-request?
You can try using UNION. Also note that group is a reserved word for MySQL.
SELECT aa.*
FROM (
SELECT id, firstname, lastname
FROM user
WHERE id IN (1, 2, 3, 4)
LIMIT 2
UNION ALL
SELECT id, firstname, lastname
FROM user
WHERE id = 5
) AS aa
select * from table_name where id=2
union all
select * from table_name where group=2

mysql query for items with multiple conditions (item options)

What I like to achieve is
a: display all Items that are in all of the selected category's
b: return / update the category list with category's available based on selection
I like items to be stored and be found by use of the adjacency list model or nested sets.
I've experimented with both and may use advice what would be the best for this case.
Currently I'm using (testing with) the adjacency list model like this:
items:
ID | item_name
====================
1 | car
2 | boat
3 | bike
items_cats: (many to many)
iid | cid
====================
1 | 1
1 | 2
1 | 4
1 | 7
2 | 1
2 | 3
2 | 4
2 | 7
3 | 1
3 | 3
3 | 4
3 | 8
categorys:
ID | cat_name | parent_id
========================
1 | safety: | 0 (0 = no parent)
2 | safe | 1
3 | dangerous | 1
4 | fun: | 0
5 | a bit | 5
6 | boring | 5
7 | funny | 5
8 | cool | 5
So its no problem to get items based on cid but how would you:
1st: selection:
1- Display all items who have cat id: cid 7 (funny)?
2- return (array/object) of all category's who have items that also contain cid 7?
Would you all do this in one query or would two be more efficient?
2nd: selection:
3- Display all items who have cat id: cid 7 and also contain cat id '3' (dangerous)
4- return (array/object) of all category's who have items that contain cid 7 and cid 3?
For selecting on multiple category's I found the flowing solution. Is this a good one and would there be to gain any performance especially when the number of category's grow?
SELECT
DISTINCT t1.product_id, t1.category_id
FROM
items_cats t1
INNER JOIN
items_cats t1b
ON t1.iid =t1b.iid
WHERE
t1.cid=3 AND
t1b.cid=7
To get a list of all items that have category ID = 7, start with your many:many table
select
i.item_name
from
items_cat ic
join items i
on ic.iid = i.id
where
ic.cid = 7
to get all categories associated with any item that has the category ID of 7, you can expand from the first and get categories associate for those item IDs
select DISTINCT
ic2.cid,
c.cat_name,
coalesce( CatParent.cat_name, "" ) as ParentCategoryName
from
( select distinct ic.iid
from items_cat ic
where ic.cid = 7 ) QualifiedItems
JOIN items_cat ic2
on QualifiedItems.iid = ic2.iid
JOIN categorys c
on ic2.cid = c.id
LEFT JOIN categorys CatParent
on c.parent_id = CatParent.ID
For 3 and 4, it would be similar, but to qualify BOTH (or anytime, more than one), you need to apply an OR, a GROUP BY and make sure that the final count matches those you were trying to qualify
select
i.item_name
from
items_cat ic
join items i
on ic.iid = i.id
where
ic.cid in( 3, 7 )
group by
i.item_name
having
count(*) = 2
So you can better understand and apply these principles, I'll leave the last one for you to try and implement... If you really get stuck, let me know... :)