Many Questions like this are already on stackoverflow but with my min knowledge of SQL i can not put it together.
My table looks like this. I have 1000 Player_Id's and 200 Stat_Id's
PlayerID | StatsID | StatValue
==================================
1 | 1 | 99
1 | 2 | 31
1 | 3 | 1
2 | 1 | 102
2 | 2 | 1
2 | 3 | 33
3 | 1 | 0
3 | 2 | 1
3 | 3 | 3,4
And I would like to get a array of users where one User object looks like this:
{playerId:1, stats:{"1":99, "2":31, "3":1, ...}}
I have tried a lot but nothing is close to what I want to get done. With this SQL i got my best result:
SELECT `PlayerID`,
GROUP_CONCAT(`StatsID` SEPARATOR ', ')
FROM `player_stats`
WHERE 1 group by `PlayerID`
Here is the Result:
Array
(
[0] => Array
(
[PlayerID] => 15895
[stats] => 1, 2, 3
)
[1] => Array
(
[PlayerID] => 21307
[stats] => 1, 2, 3
)
How can I bring the StatValue Columns into my Query?
Would it be better to generate the desired object after the SQL Query or with multiple Queries?
Performance is not very crucial but of course it would be nice if generating the "playerId-array" would not take ages.
Thanks!
You can actually just add the columns to the GROUP_CONCAT():
SELECT `PlayerID`, GROUP_CONCAT(`StatsID`, ':', StatValue SEPARATOR ', ') as stats
FROM `player_stats`
GROUP BY `PlayerID`;
Try
SELECT `PlayerID`, `StatsValues` FROM (
SELECT `PlayerID`, CONCAT(`StatsID`, ',', GROUP_CONCAT(`StatsValues` SEPARATOR ', ')) AS `StatsValues`
FROM tblName
GROUP BY `PlayerID`, `StatsID`);
Related
My MySQL table having column with comma separated numbers. See below example -
| style_ids |
| ---------- |
| 5,3,10,2,7 |
| 1,5,12,9 |
| 6,3,5,9,4 |
| 8,3,5,7,12 |
| 7,4,9,3,5 |
So my expected result should have top 5 numbers with maximum appearance count in descending order as 5 rows as below -
| number | appearance_count_in_all_rows |
| -------|----------------------------- |
| 5 | 5 |
| 3 | 4 |
| 9 | 3 |
| 7 | 2 |
| 4 | 2 |
Is it possible to get above result by MySQL query ?
As already alluded to in the comments, this is a really bad idea. But here is one way of doing it -
WITH RECURSIVE seq (n) AS (
SELECT 1 UNION ALL SELECT n+1 FROM seq WHERE n < 20
), tbl (style_ids) AS (
SELECT '5,3,10,2,7' UNION ALL
SELECT '1,5,12,9' UNION ALL
SELECT '6,3,5,9,4' UNION ALL
SELECT '8,3,5,7,12' UNION ALL
SELECT '7,4,9,3,5'
)
SELECT seq.n, COUNT(*) appearance_count_in_all_rows
FROM seq
JOIN tbl ON FIND_IN_SET(seq.n, tbl.style_ids)
GROUP BY seq.n
ORDER BY appearance_count_in_all_rows DESC
LIMIT 5;
Just replace the tbl cte with your table.
As already pointed out you should fix the data if possible.
For further details read Is storing a delimited list in a database column really that bad?.
You could use below answer which is well explained here and a working fiddle can be found here.
Try,
select distinct_nr,count(distinct_nr) as appearance_count_in_all_rows
from ( select substring_index(substring_index(style_ids, ',', n), ',', -1) as distinct_nr
from test
join numbers on char_length(style_ids) - char_length(replace(style_ids, ',', '')) >= n - 1
) x
group by distinct_nr
order by appearance_count_in_all_rows desc ;
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';
I have table like this:
-----------
ID | Value
-----------
1 | AAAA
2 | ABCD
3 | AADC
4 | ABBD
I am trying to figure out how to return the number of times a string occurs in each of the Value.
So, if I want to count of time 'A' and 'B'appears, the sql statement will return like this:
-------------------
ID | Value | Count
-------------------
1 | AAAA | 0
2 | ABCD | 1
3 | AADC | 0
4 | ABBD | 2
5 | ABBB | 3
6 | AABB | 3
7 | AAAB | 3
Is there any way to do this? I do not want to use php, vb, etc. Just MySQL
Seems you want to count the values and then combine the result. I believe something like this will work for you.
SQLFiddle
SELECT
id,
value,
ROUND (
(
LENGTH(value)
- LENGTH(REPLACE(value, "A", ""))
) / LENGTH("A")
) AS count
FROM chars
UNION ALL
SELECT
id,
value,
ROUND (
(
LENGTH(value)
- LENGTH(REPLACE(value, "B", ""))
) / LENGTH("B")
) AS count
FROM chars
You can try this mate:
SELECT
ID,
Value,
LENGTH(REPLACE(Value, 'A', '')) 'count_a',
LENGTH(REPLACE(Value, 'B', '')) 'count_b'
FROM
your_table;
or this one:
SELECT
ID,
Value,
LENGTH(REPLACE(Value, IF(LENGTH(REPLACE(Value, 'A','')) = 3, 'A', 'B'), '')) 'Count',
FROM
your_table;
This one is based on the given expected result
I need to do an advanced selection in SQL, but I'm stuck.
I have the following table:
id | user_id | position | value
1 | 1 | 1 | 1
1 | 1 | 2 | 1
1 | 1 | 3 | 3
1 | 2 | 1 | 2
1 | 2 | 2 | 2
1 | 2 | 3 | 2
1 | 3 | 1 | 3
1 | 3 | 2 | 2
1 | 3 | 3 | 1
I need a query that gives me a result set ordered as this:
Total sum for each user (user 1: 5, user 2: 6, user 3: 6)
Value for position 3 for each user (user 1: 3, user 2: 2, user 3: 1)
Val for pos 3 + val for pos 2 for each user (user 1: 4, user 2: 4, user 3: 4)
Val for pos 3 + val for pos 2 + val for pos 1 for each user (user 1: 5, user 2: 6, user 3: 6)
This is just an example, the table can actually contain more positions, so I need a query that is not hard coded on three positions.
NOTE: There is always the same number of positions for each user_id. In this example it's three, but I could as well truncate the table and add data for each user using five positions.
An ugly solution is to assume that there are never no more than ten positions, creating pos1, pos2, and so on as columns and just add them accordingly in the query. If you only use three positions you get a lot of NULL values and you also get stuck with a maximum of ten positions.
I have considered the use of temporary tables, but haven't found a breakthrough there either.
How would you do it?
I need a query that is not hard coded on three positions.
Then you can't output the subtotals in columns. SQL requires that the columns are fixed at the time you prepare the query; you can't write a query that appends more columns dynamically as it discovers how many distinct values are in the data.
You can, however, output a dynamic number of rows.
SELECT t1.user_id, CONCAT(t1.position, '-', MAX(t2.position)) AS position_range,
SUM(t2.value) AS subtotal
FROM MyTable t1
INNER JOIN MyTable t2
ON t1.user_id = t2.user_id AND t1.position <= t2.position
GROUP BY t1.user_id, t1.position;
The output is:
+---------+----------------+----------+
| user_id | position_range | subtotal |
+---------+----------------+----------+
| 1 | 1-3 | 5 |
| 1 | 2-3 | 4 |
| 1 | 3-3 | 3 |
| 2 | 1-3 | 6 |
| 2 | 2-3 | 4 |
| 2 | 3-3 | 2 |
| 3 | 1-3 | 6 |
| 3 | 2-3 | 3 |
| 3 | 3-3 | 1 |
+---------+----------------+----------+
You'll have to write application code to pivot this into columns after you fetch the whole result set.
Sorry, there is no way to write a fully dynamic pivot query in any brand of RDBMS. You have two choices:
Write code to generate the SQL based on data, as shown in #TimLehner's updated answer
Write code to post-process a general-purpose query like the one I show above.
You can potentially do something like this:
select user_id
, sum(value) as value_sum
, (select value from my_table where user_id = t.user_id and position = 3) as pos_3_val
, (select sum(value) from my_table where user_id = t.user_id and position >= 2) as pos_2_3_val
, (select sum(value) from my_table where user_id = t.user_id and position >= 1) as pos_1_2_3_val
from my_table as t
group by user_id
order by user_id
I think this should work in most any RDBMS.
If it has to by dynamic, you could potentially create this query in stored procedure or your application and run it.
You could also dynamically pivot your results from a query like this:
select *
, (
select sum(value)
from my_table
where user_id = t.user_id
and position >= t.position
) as running_total_descending
from my_table t
Please let us know if any of this works, and if you have trouble creating a dynamic version (and which RDBMS).
UPDATE
Now that we know the RDBMS (MySQL) we can have a specific dynamic version:
set #sql = null;
select
group_concat(distinct
concat(
' sum(case when position >= ',
position,
' then value end) as pos_',
position,
'_plus'
)
) into #sql
from my_table;
set #sql = concat('select user_id,', #sql, ' from my_table t group by user_id;');
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;
SQL Fiddle
Special thanks to #bluefeet for posting this type of solution often.
I should also note that many devs believe this type of pivoting often belongs in the application or front-end. I'm no exception, both for separation of concerns and because your app can generally scale better than your OLTP database.
Let's say I have such a table (ordered by date):
id | name | type | date
1 | A | 1 | 01-08-2012
2 | A | 2 | 01-08-2012
3 | B | 1 | 02-09-2012
4 | A | 1 | 01-10-2012
5 | A | 4 | 01-10-2012
6 | A | 5 | 02-10-2012
I want to group subsequent rows that have the same 'name' value and count them:
name | count
A | 2
B | 1
A | 3
I was thinking about writing a stored procedure and using cursors, but I was also wondering, if there's a simpler solution, for example using nested SELECTs, etc.
My question is very similar to: how to group array and count them, but that one concerns PHP.
To do that I used a couple of variables,
the table structure, I created my own just for testing and it's:
create table abc (id int, name varchar(20),type int);
insert into abc values
( 1 , 'A' , 1 ),
( 2 , 'A' , 2 ),
( 3 , 'B' , 1 ),
( 4 , 'A' , 1 ),
( 5 , 'A' , 4 ),
( 6 , 'A' , 5 )
the query ended being like this:
set #a:='';
set #counter:=1;
set #groupby:=0;
select *,count(REPEATED) from (select name,if(#a=name,#counter:=#counter+1,#counter:=1) as rep,if(#counter=1,#groupby:=#groupby+1,#groupby) as repeated,#a:=name type from abc) as t group by repeated
you can see it works in SQLFIDDLE if you have any question let me know.
In the SQLFIDDLE