Concatenate SQL results into a single string with a counter - mysql

Say I have a table as shown below
Product_Id Description
BX01 Desc 1
BxX1 Desc 2
Dss3 Desc 3
HHXY Desc 4
I want the result exactly like: 1 - BX01, 2 - BxX1, 3 - Dss3, 4 - HHXY
I have this query:
DECLARE #ProID VARCHAR(8000)
SELECT #ProID = COALESCE(#ProID + ' - ', '') + Product_Id FROM TABLE
SELECT #ProID
but the return values is only :
BX01,- BxX1,- Dss3,- HHXY.
The counting is lacking.
How to do that?
thanks

Try this instead:
SELECT GROUP_CONCAT(
CONCAT( #rownum := #rownum + 1, '-', Product_Id )
SEPARATOR ','
) AS data
FROM table, ( select #rownum := 0 ) r
SQL Fiddle Demo
This will build the result you wanted by grouping all the column data and adding a counter before it, separating each data with a comma.
Note: You can remove , ( select #rownum := 0 ) r though. Depends on what you want.

In mysql it would be ,
SET #a:=0;
SELECT CONCAT( #a:=#a+1,'-',Product_Id) FROM TABLE ;

Related

ranking of data based on multiple columns in mysql with using rank function

i am very new to mysql and tried to rank top records based on two fields
sharing you the current script and output along with the desired output
Current Script :
select u_rank, c_rank,u_name,c_name,
#curRank := #curRank + 1 AS rank
from (
SELECT u_rank, c_rank,u_name,c_name
from abc
) a,
(
select #curRank := 0
) r
order by c_rank,u_rank
Current OutpuT
MY DESIRED OUTPUT IS
Please help
If I am decifering your question correctly, you are wanting to rank the c_ranks ascending or each u_name and then order by that rank then the u_rank. After that you need to get a rank for all records in that order. I'm not sure that the way I just worded that made any sense... but the following should get that result for you:
SET #rank1 = 0;
SET #rank2 = 0;
SET #u_name = '';
SELECT u_rank, c_rank, u_name, c_name, #rank2 := #rank2 + 1 AS rank
FROM (
SELECT u_rank,
c_rank,
c_name,
#rank1 := (#rank1 + 1 - IF(#u_name = u_name, 0, #rank1)) AS rank1,
#u_name := u_name AS u_name
FROM abc
ORDER BY u_name, c_rank
) foo
ORDER BY rank1, u_rank;

MySQL leaderboard with rank

I have a leaderboard where i want to give each row a rank for how many points they have. This is the code i have so far. It works fine but when two players have the same amount of points, the rank is the same, for example if there are 2/2 players in the DB where they both have 100 points, both their ranks are 1.
I would like to even if they have same amount of points to give one of them a higher rank, so it would be 1,2.
SELECT id, leaderfirst.pictureid, leaderfirst.point, FIND_IN_SET( leaderfirst.point, (
SELECT GROUP_CONCAT( leaderfirst.point
ORDER BY leaderfirst.point DESC )
FROM leaderfirst )
) AS rank
FROM leaderfirst
leaderboard =
SELECT id,
leaderfirst.pictureid,
leaderfirst.point,
FIND_IN_SET( leaderfirst.point,
( SELECT GROUP_CONCAT(
leaderfirst.point ORDER BY leaderfirst.point DESC ,id)
FROM leaderfirst ) ) AS rank
FROM leaderfirst
Try this.
What you want here is an increment counter in the SQL:
There are two ways of doing this:
First:
set #rownum := 0;
And then
SELECT id, leaderfirst.pictureid, leaderfirst.point,
FIND_IN_SET( leaderfirst.point,
( SELECT GROUP_CONCAT(
leaderfirst.point ORDER BY leaderfirst.point DESC
)
FROM leaderfirst )
) AS rank,
#rownum := #rownum + 1 AS row_number
FROM leaderfirst
ORDER BY row_number
Or:
Combining the first two queries together you can use :
SELECT id, leaderfirst.pictureid, leaderfirst.point,
FIND_IN_SET( leaderfirst.point,
( SELECT GROUP_CONCAT(
leaderfirst.point ORDER BY leaderfirst.point DESC
)
FROM leaderfirst )
) AS rank,
#rownum := #rownum + 1 AS row_number
FROM leaderfirst
CROSS JOIN (select #rownum := 0) r
ORDER BY row_number
Source: This answer here.
There are also various variaton on this theme.
Solution 2:
simply ignore trying to generate this data with your MySQL; you said you don't care about order, simply the auto-increment nature of the resultant ranking; therefore you can do something like this if you're using PHP (and similar work in other languages):
Pseudo-code below:
$mysqlDataOutput = mysql result data from your SQL . "ORDER BY rank DESC";
$counter = 0;
foreach($mysqlDataOutput as $resultRow){
print $counter.") ".resultRow['points']. " -
<img src='".$resultRow['pictureid']."' alt=''>";
$counter++;
}
unset($mysqlDataOutput, $resultRow);
If you want to order the other way around (lowest rank first) then simply invert your SQL ORDER BY and set:
$counter = count($mysqlDataOutput);
foreach(){
...
$coounter--;
}

Mysql recursive substracting and multiplying values

Couldn't really explain my problem with words, but with an example I can show it clearly:
I have a table like this:
id num val
0 3 10
1 5 12
2 7 12
3 11 15
And I want to go through all the rows, and calculate the increase of the "num", and multiply that difference with the "val" value. And when I calculated all of these, I want to add these results together.
This is the mathematical equation, that I want to run on the table:
Result = (3-0)*10 + (5-3)*12 + (7-5)*12 + (11-7)*15
138 = Result
Thank you.
You can do with mysql variables, but you will still get one record for each entry.
select
#lastTotal := #lastTotal + ( (yt.num - #lastNum) * yt.val ) thisLineTotal,
#lastNum := yt.num as saveForNextRow,
yt.id
from
yourTable yt,
( select #lastTotal := 0,
#lastNum := 0 ) sqlvars
order by
id
This SHOULD give you what you want to confirm the calculations to each record basis.
Now, to get the one record and one column result, you can wrap it such as
select
pq.thisLineTotal
from
(above entire query ) as pq
order by
pq.id DESC
limit 1
Assuming the IDs are consecutive as your sample data suggests, just join the table to itself:
select sum((t1.num-ifnull(t2.num,0))*t1.val) YourValue
from YourTable t1
left join YourTable t2
on t2.id = t1.id - 1;
http://www.sqlfiddle.com/#!2/40b9f/12
This will give you the total. Make sure to order in the order you wish - I have ordered by id
SET #runtot:=0;
SET #prevval:=0;
select max(rt) as total FROM (
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID) tot
If you want to see the details of the calculation, leave out the outer select as so:
SET #runtot:=0;
SET #prevval:=0;
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID
If it is possible to have negative numbers for your column values, using max(rt) won't work for the total. You should then use:
SET #runtot:=0;
SET #prevval:=0;
select #runtot as total FROM (
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID) tot LIMIT 1

mysql percentile rank by group

I have a table containing date, id, and value, with about 1000 id rows per date. I need to calculate the percentile rank of each row, by date. I am using the following code for percentile rank for a single date, but with over 10 years of daily data this is very inefficient to run date-by-date. Seems that it should be able to be formulated in MySQL but I've not been able to make it work.
Date ID Value
date1 01 -7.2
date1 02 0.6
date2 01 1.2
date2 02 3.8
SELECT c.id, c.value, ROUND( (
(#rank - rank) / #rank ) *100, 2) AS rank
FROM (
SELECT * , #prev := #curr , #curr := a.value,
#nxtRnk := #nxtRnk + 1,
#rank := IF( #prev = #curr , #rank , #nxtRnk ) AS rank
FROM (
SELECT id, value
FROM temp
WHERE date = '2013-06-28'
) AS a, (
SELECT #curr := NULL , #prev := NULL , #rank :=0, #nxtRnk :=0
) AS b
ORDER BY value DESC
) AS c
So basically I want to SELECT DISTINCT(date), and then for each date perform the above SELECT, which is preceeded by INSERT INTO table2( ... ) to write the results to table2.
Thanks for any help,
Hugh
I finally developed an acceptable solution by using a temporary table. Maybe not the optimum solution, but it works in about 5 sec on a million + record table.
My temporary table (t1) contains date and the count of rows for date.
The third select above is changed to
SELECT t1.date, t1.cnt, id, value FROM t1 LEFT JOIN temp ON(t1.date = temp.date)
Also, the calculations in the first SELECT above were changed to use c.cnt rather than #rank, and an #prevDate variable was created to reset the rank count on date changes.
Thanks to anyone who looked at this and tried to work up a solution.
I was trying to solve this for quite some time and then I found the following answer. Honestly brilliant. Also quite fast even for big tables (the table where I used it contained approx 5 mil records and needed a couple of seconds).
SELECT
CAST(SUBSTRING_INDEX(SUBSTRING_INDEX( GROUP_CONCAT(field_name ORDER BY
field_name SEPARATOR ','), ',', 95/100 * COUNT(*) + 1), ',', -1) AS DECIMAL)
AS 95th Per
FROM table_name;
As you can imagine just replace table_name and field_name with your table's and column's names.
For further information check Roland Bouman's original post

MySql Query: Select top 3 rows from table for each category

I have a table with records and it has a row called category. I have inserted too many articles and I want to select only two articles from each category.
I tried to do something like this:
I created a view:
CREATE VIEW limitrows AS
SELECT * FROM tbl_artikujt ORDER BY articleid DESC LIMIT 2
Then I created this query:
SELECT *
FROM tbl_artikujt
WHERE
artikullid IN
(
SELECT artikullid
FROM limitrows
ORDER BY category DESC
)
ORDER BY category DESC;
But this is not working and is giving me only two records?
LIMIT only stops the number of results the statement returns. What you're looking for is generally called analytic/windowing/ranking functions - which MySQL doesn't support but you can emulate using variables:
SELECT x.*
FROM (SELECT t.*,
CASE
WHEN #category != t.category THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#category := t.category AS var_category
FROM TBL_ARTIKUJT t
JOIN (SELECT #rownum := NULL, #category := '') r
ORDER BY t.category) x
WHERE x.rank <= 3
If you don't change SELECT x.*, the result set will include the rank and var_category values - you'll have to specify the columns you really want if this isn't the case.
SELECT * FROM (
SELECT VD.`cat_id` ,
#cat_count := IF( (#cat_id = VD.`cat_id`), #cat_count + 1, 1 ) AS 'DUMMY1',
#cat_id := VD.`cat_id` AS 'DUMMY2',
#cat_count AS 'CAT_COUNT'
FROM videos VD
INNER JOIN categories CT ON CT.`cat_id` = VD.`cat_id`
,(SELECT #cat_count :=1, #cat_id :=-1) AS CID
ORDER BY VD.`cat_id` ASC ) AS `CAT_DETAILS`
WHERE `CAT_COUNT` < 4
------- STEP FOLLOW ----------
1 . select * from ( 'FILTER_DATA_HERE' ) WHERE 'COLUMN_COUNT_CONDITION_HERE'
2. 'FILTER_DATA_HERE'
1. pass 2 variable #cat_count=1 and #cat_id = -1
2. If (#cat_id "match" column_cat_id value)
Then #cat_count = #cat_count + 1
ELSE #cat_count = 1
3. SET #cat_id = column_cat_id
3. 'COLUMN_COUNT_CONDITION_HERE'
1. count_column < count_number
4. ' EXTRA THING '
1. If you want to execute more than one statement inside " if stmt "
2. IF(condition, stmt1 , stmt2 )
1. stmt1 :- CONCAT(exp1, exp2, exp3)
2. stmt2 :- CONCAT(exp1, exp2, exp3)
3. Final "If" Stmt LIKE
1. IF ( condition , CONCAT(exp1, exp2, exp3) , CONCAT(exp1, exp2, exp3) )
share
Use group by instead of order by.