How do I fix this query to multiply a column value - mysql

I have this table
select * from points
+---------+------+------+
| NAME | Type | RANK |
+---------+------+------+
| A | H | 90 |
| A | M | 100 |
| A | H | N/A |
| A | H | N/A |
| A | H | N/A |
| B | H | 100 |
| B | M | 100 |
| B | L | 100 |
| C | H | 85 |
| C | M | 100 |
+---------+------+------+
I'm using this query
SELECT name,
CAST(
( -- only have H, or only have M, or only have L:
CASE WHEN `# of H` = 0 AND `# of M` = 0 THEN 100 * `# of active L` / `# of L`
WHEN `# of H` = 0 AND `# of L` = 0 THEN 100 * `# of active M` / `# of M`
WHEN `# of M` = 0 AND `# of L` = 0 THEN 100 * `# of active H` / `# of H`
-- only have H & M, or only have H & L, or only have M & L:
WHEN `# of H` = 0 THEN 60 * `# of active M` / `# of M` + 40 * `# of active L` / `# of L`
WHEN `# of M` = 0 THEN 90 * `# of active H` / `# of H` + 20 * `# of active L` / `# of L`
WHEN `# of L` = 0 THEN 80 * `# of active H` / `# of H` + 20 * `# of active M` / `# of M`
-- have all three:
ELSE 70 * `# of active H` / `# of H` + 20 * `# of active M` / `# of M` + 10 * `# of active L` / `# of L`
END
) AS SIGNED ) AS score
FROM ( SELECT name,
SUM(IF( type = 'H', 1, 0)) AS `# of H`,
SUM(IF(rank AND type = 'H', 1, 0)) AS `# of active H`,
SUM(IF( type = 'M', 1, 0)) AS `# of M`,
SUM(IF(rank AND type = 'M', 1, 0)) AS `# of active M`,
SUM(IF( type = 'L', 1, 0)) AS `# of L`,
SUM(IF(rank AND type = 'L', 1, 0)) AS `# of active L`
FROM points
GROUP BY name
) t
ORDER
BY name
;
I get this Output
+---------+-------+
| NAME | SCORE |
+---------+-------+
| A | 60 | <--[(2xH)=40 + (1xM)=20] =60
| B | 100 | <--[(1xH)=70 + (1xM)=20 + (1xL)=10] =100
| C | 100 | <--[(1xH)=80 + (1xM)=20] =100
+---------+-------+
I need this Desired output
+---------+-------+
| NAME | SCORE |
+---------+-------+
| A | 36 | <--[70/4=(17.5 per H) therefore (17.5)*(rank of that h: 90%)=15.75 + (M values, which equals 20/1 =20 Therefore: rank of that m:100% * 20 = 100) = 36 rounded
| B | 100 | <--[(1xH)=70 + (1xM)=20 + (1xL)=10] =100
| C | 88 | <--[(1xH)=80 + (1xM)=20] =100
+---------+-------+
Computations required:
Type can have only three values: {H, M, L};
When all values are present, they are graded as followed:
H=70 M=20 L=10
If an name has more than one kind of Type (H, M, or L) then points are distributed as followed:
H/(number of H) ; M/(number of M); L/(number of L)
-- Example: A has 4 H therefore 70 / 4 = 17.5 for each H
But some names have a complete set with out having all 'Types.
-- example : C has Type values: 'H&M` only
Now Type 'H' and 'M' have to equal 100 for C.
So when only 'H` and 'M' are present they are graded as followed:
H=80 M=20
Equally if another animal comes along with only two Type values M & L they will be graded as followed:
M=60 L=40
Equally if another animal comes along with only two Type values H & L they will be graded as followed:
H=90 L=10
And also
if only H is presnet H=100
if only M is presnet M=100
if only L is presnet L=100

This looks familiar. :-)
Your description is inconsistent in a number of places — for example, your "desired output" for A uses 70 and 20, even though no As have type L — but if you mean what I think you do, then the main change you need is to change SUM(IF(rank AND type = 'H', 1, 0)) AS `# of active H` to SUM(IF(type = 'H', rank / 100.0, 0)) AS `rank of H` (and likewise for M and L), and change all references to `# of active H` to refer to `rank of H` instead. This way each record will be included in proportion to its rank, rather than being an all-or-nothing thing.
You'll also want to use ROUND instead of CAST — or in addition to CAST — when converting your score to an integer.

Related

Can these two SQL queries be combined?

I'm wondering if there's a way I can combine these two queries into one? I need to get the mean and std dev for each column in the company_feature table. I then need to take those two values and use them in an aggregation query on each row in the company_feature table.
/* Get mean and std dev for each feature column */
SELECT
AVG(F1) AS F1_mean,
STDDEV(F1) AS F1_std_dev
FROM company_feature_test cft;
/* Add averages for each feature to the following query */
SELECT
DATA.company_id,
(
CASE
WHEN DATA.in_ref_set = 0 AND DATA.size = 'SMALL'
THEN
1 * ((LN(DATA.F1 + 1) - :F1_mean) / :F1_std_dev ) * 1
WHEN DATA.in_ref_set = 0 AND DATA.size = 'MEDIUM'
THEN
2 * ((LN(DATA.F1 + 1) - :F1_mean) / :F1_std_dev ) * 2
WHEN DATA.in_ref_set = 0 AND DATA.size = 'LARGE'
THEN
3 * ((LN(DATA.F1 + 1) - :F1_mean) / :F1_std_dev ) * 3
WHEN DATA.in_ref_set = 0 AND DATA.size = 'VERY_LARGE'
THEN
4 * ((LN(DATA.F1 + 1) - :F1_mean) / :F1_std_dev ) * 4
ELSE
5 * ((LN(DATA.F1 + 1) - :F1_mean) / :F1_std_dev )
END
) AS feature_1
FROM (
SELECT company.in_ref_set, company.size, cft.*
FROM company_feature_test cft
JOIN company ON company.id = cft.company_id
GROUP BY company.id
) AS DATA
GROUP BY DATA.company_id;
the tables look like the following (below). There is a relation between company.id and company_feature.company_id.
company table
| id | ref_set | size |
| -- | --- | --- |
| 1 | 0 | SMALL |
| 2 | 1 | LARGE |
company_feature table
| company_id | F1 | F2 |
| --- | --- | --- |
| 1 | 5 | 10 |
| 2 | 15 | 20 |
The query outputs the following data:
| company_id | feature_1 |
| --- | --- |
| 1 | -1.66 |
| 2 | -1.44 |
Yes, you just cross join them:
SELECT
DATA.company_id,
(
CASE
WHEN DATA.in_ref_set = 0 AND DATA.size = 'SMALL'
THEN
1 * ((LN(DATA.F1 + 1) - TOTALS.F1_mean) / TOTALS.F1_std_dev ) * 1
WHEN DATA.in_ref_set = 0 AND DATA.size = 'MEDIUM'
THEN
2 * ((LN(DATA.F1 + 1) - TOTALS.F1_mean) / TOTALS.F1_std_dev ) * 2
WHEN DATA.in_ref_set = 0 AND DATA.size = 'LARGE'
THEN
3 * ((LN(DATA.F1 + 1) - TOTALS.F1_mean) / TOTALS.F1_std_dev ) * 3
WHEN DATA.in_ref_set = 0 AND DATA.size = 'VERY_LARGE'
THEN
4 * ((LN(DATA.F1 + 1) - TOTALS.F1_mean) / TOTALS.F1_std_dev ) * 4
ELSE
5 * ((LN(DATA.F1 + 1) - TOTALS.F1_mean) / TOTALS.F1_std_dev )
END
) AS feature_1
FROM (
SELECT company.in_ref_set, company.size, cft.*
FROM company_feature_test cft
JOIN company ON company.id = cft.company_id
GROUP BY company.id
) AS DATA
CROSS JOIN (
SELECT
AVG(F1) AS F1_mean,
STDDEV(F1) AS F1_std_dev
FROM company_feature_test cft
) AS TOTALS
Note that there's no need to group by in the outer query; there will already only be one row per company.
Note that you still seem to be doing conditional aggregation incorrectly, if that is what you are trying to do; assuming there are multiple rows in cft for each company, you will be selecting an arbitrary F1 for each company. Default settings in newer versions of mysql will prohibit this.

count product for different discount range

I have Products table with discount like
productid discount
1 12
2 22
3 32
4 45
5 55
6 57
7 63
8 72
9 85
i want sql query for product count where discount 10%-100% or discount 20%-100% or discount 30%-100% or discount 40%-100% so on .. discount range
My sql query is
SELECT Count(product_id) AS product_count,
Substring_index(value_range, '-', 1) AS start,
Substring_index(value_range, '-', -1) AS END
FROM (SELECT product_id,
discount,
CASE
WHEN discount BETWEEN 80 AND 90 THEN '80-90'
WHEN discount BETWEEN 70 AND 90 THEN '70-90'
WHEN discount BETWEEN 60 AND 90 THEN '60-90'
WHEN discount BETWEEN 50 AND 90 THEN '50-90'
WHEN discount BETWEEN 40 AND 90 THEN '40-90'
WHEN discount BETWEEN 30 AND 90 THEN '30-90'
WHEN discount BETWEEN 20 AND 90 THEN '20-90'
WHEN discount BETWEEN 10 AND 90 THEN '10-90'
END AS value_range
FROM products) AS T2
GROUP BY value_range
ORDER BY Cast(start AS UNSIGNED) ASC
but it not giving desire result
expected result is
discount_range countproduct
10%-100% 9
20%-100% 8
30%-100% 7
40%-100% 6.. so on
Here is My output.
First you need create a table for your ranges:
CREATE TABLE Ranges
(`start` int, `end` int)
;
INSERT INTO Ranges
(`start`, `end`)
VALUES
(10, 100),
(20, 100),
(30, 100),
(40, 100),
(50, 100),
(60, 100),
(70, 100),
(80, 100),
(90, 100)
;
Then just found on what ranges each product discount is part of:
SELECT `start`, `end`, `productid`, `discount`
FROM ranges
LEFT JOIN products
ON products.discount between `start` and `end`
Then just count it:
SQL DEMO
SELECT `start`, `end`, COUNT(`productid`)
FROM (
SELECT `start`, `end`, `productid`, `discount`
FROM ranges
LEFT JOIN products
ON products.discount between `start` and `end`
) t
GROUP BY `start`, `end`
ORDER BY `start`
OUTPUT
| start | end | COUNT(`productid`) |
|-------|-----|--------------------|
| 10 | 100 | 9 |
| 20 | 100 | 8 |
| 30 | 100 | 7 |
| 40 | 100 | 6 |
| 50 | 100 | 5 |
| 60 | 100 | 3 |
| 70 | 100 | 2 |
| 80 | 100 | 1 |
| 90 | 100 | 0 |
You need a query that counts the products inside the query that groups by range:
select
concat(t.floorvalue, '%-90%') discount_range,
t.countproduct
from (
select
floor(p.discount / 10) * 10 floorvalue,
(select count(*) from products where discount >= floorvalue) countproduct
from products p
group by floorvalue
) t
order by discount_range
See the demo.
Results:
| discount_range | countproduct |
| -------------- | ------------ |
| 10%-90% | 9 |
| 20%-90% | 8 |
| 30%-90% | 7 |
| 40%-90% | 6 |
| 50%-90% | 5 |
| 60%-90% | 3 |
| 70%-90% | 2 |
| 80%-90% | 1 |
try this one
select
x.bin,
x.bin_count,
sum(y.bin_count) as cumulative_count
from
( select
floor(discount / 10) as lower_bound,
concat('[', floor(discount / 10) * 10, ' - ', floor(discount / 10) * 10 + 10, ')') as bin,
count(*) as bin_count
from
t1
group by
1, 2) as x
join ( select
floor(discount / 10) as lower_bound,
concat('[', floor(discount / 10) * 10, ' - ', floor(discount / 10) * 10 + 10, ')') as bin,
count(*) as bin_count
from
t1
group by
1, 2) as y on x.lower_bound >= y.lower_bound
group by
1, 2
one more
select
x.bin,
x.bin_count,
concat('[', min(y.lower_bound), ' - ', max(y.upper_bound), ')') as cumulative_bin,
sum(y.bin_count) as cumulative_count
from
( select
floor(discount / 10) * 10 as lower_bound,
floor(discount / 10) * 10 + 10 as upper_bound,
concat('[', floor(discount / 10) * 10, ' - ', floor(discount / 10) * 10 + 10, ')') as bin,
count(*) as bin_count
from
t1
group by
1, 2, 3) as x
join ( select
floor(discount / 10) * 10 as lower_bound,
floor(discount / 10) * 10 + 10 as upper_bound,
concat('[', floor(discount / 10) * 10, ' - ', floor(discount / 10) * 10 + 10, ')') as bin,
count(*) as bin_count
from
t1
group by
1, 2, 3) as y on x.lower_bound >= y.lower_bound-- order by 1, 5
where
x.lower_bound >= 10
and y.lower_bound >= 10
group by
x.bin
If you don't want to create a second table, try building off of this:
select "10%-100%" as discount_range, (select count(*) from products where discount between 10 and 100) as product_count
union
select "20%-100%" as discount_range, (select count(*) from products where discount between 20 and 100) as product_count
union
select "30%-100%" as discount_range, (select count(*) from products where discount between 30 and 100) as product_count
union
select "40%-100%" as discount_range, (select count(*) from products where discount between 40 and 100) as product_count

Current month each day stats per user row

I have 23 users in group 75(user_group).
Then I have table users_participated where row is set only if user participated ( sometimes it exists but is 0 ( not participate ) ).
Now I need to show each day for every user this month did he participate or not.
I can do it with php loop. But I was wondering if there is a way to do it only with single mysql query.
//output html table should look like y = yes n = no 1,2,3,5 are days in month
user | 1 | 2 | 3 | 4 | 5..
user1| y | n | y | n | n
user2| n | y | y | n | y
// user table
id | user | group
1 | user1 | 75
2 | user2 | 75
3 | user3 | 75
4 | user4 | 68
//participate table
user_id | participated | date_added
1 | 1 | 2014-03-29 11:03:00
2 | 0 | 2014-03-29 11:03:00
//sometimes it can be 0 AND sometimes if its null its not even in this table if u know what i mean.
How can I do this ?
A simple crosstab query can do that:
SELECT u.user,
max( if(day(p.date_added) = 1, p.participated, 0 ) ) day1,
max( if(day(p.date_added) = 2, p.participated, 0 ) ) day2,
..............
..............
..............
max( if(day(p.date_added) = 28, p.participated, 0 ) ) day28,
max( if(day(p.date_added) = 29, p.participated, 0 ) ) day29,
max( if(day(p.date_added) = 30, p.participated, 0 ) ) day30,
max( if(day(p.date_added) = 31, p.participated, 0 ) ) day31
FROM user u
JOIN participate p
ON u.id = p.user_id
WHERE p.date_added >= '2014-03-01'
AND p.date_added < '2014-04-01'
GROUP BY u.id
demo: http://sqlfiddle.com/#!9/cd31f/3

mysql - count positive consecutive value

records:
ID | NAME | VALUE
---|-------|-------
1 | ALPHA | 5
2 | ALPHA | 7 //comment: [2 times positive numbers]
3 | ALPHA | -4
4 | ALPHA | 3 //comment: [1 times positive numbers]
5 | ALPHA | -2
6 | ALPHA | -3
7 | ALPHA | 9
8 | ALPHA | 3 //comment: [2 times positive numbers]
9 | ALPHA | -2
10 | ALPHA | -6
I need to know how many consecutive time I have a positive number so in this case we have:
2 (consecutive positive number)
1 (consecutive positive number)
2 (consecutive positive number)
the final result that I want is show output with a table that tell me how many time in table we have 1 consecutive number, 2consecutive numbers, 3consecutive numbers, ...
so a table like this:
table structure:
consecutive number
value
data:
1 | 1 (we have 1 times, 1 consecutive numbers)
2 | 2 (we have 2 times, 2 consecutive numbers)
3 | 0 (we have 0 times, 3 consecutive numbers)
Try this:
SELECT x consecutive_numbers,
count(*) how_many_times
FROM (
SELECT y, max( x ) x
FROM (
SELECT
if( value < 0, #x:=0, #x:=#x+1 ) x,
if( value < 0, #y:=#y+1, #y ) y,
value
FROM table1
CROSS JOIN (
select #x:=0, #y:=0
) var
ORDER BY id
)q
WHERE value >= 0
GROUP BY y
) qq
GROUP BY x
;
Demo: --> http://www.sqlfiddle.com/#!2/75fa7/8
SELECT peak as consecutive_number, COUNT(*) AS value
FROM (
SELECT IF(value <= 0 AND #counter > 0, #counter, NULL) AS peak, #counter := IF(value <= 0, 0, #counter+1) AS counter
FROM (SELECT *
FROM (SELECT value
FROM mytable
ORDER BY id) x
UNION ALL
SELECT -1) x -- in case last row is positive
CROSS JOIN (SELECT #counter := 0) var
) x
WHERE peak IS NOT NULL
GROUP BY peak
DEMO

mysql join two really complex queries

I currently have two really complex queries that I use to bring in scores with ranks. The first is all the participants and their scores with stage ranks, the second is Grouped by each participant and calculates their overall rank (sum of stage ranks).
First query to get all participants for each stage with scores and ranks
SELECT * FROM (
SELECT alias, catabbr, sh_dq, raw, sdq, dnf,
#rownum := IF(#stage != stage_name, 1, #rownum + 1) AS rowNum,
#rank := IF(#prevVal != cTime, #rownum, #rank) AS rank,
#stage := stage_name as stage_name,
#prevVal := cTime AS cTime,
id, zeroTime
FROM
(SELECT #rownum:= 0) rn,
(SELECT #rank:= 1) av,
(SELECT #stage:= '') sv,
(SELECT #prevVal:= 0) pv,
(SELECT s.stage_name stage_name, sc.alias alias, sc.catAbbr catabbr, sc.sh_dq sh_dq, sc.time raw, sc.id id, sc.sdq sdq, sc.dnf dnf,
IF(sc.time = "0", 1, 0) AS zeroTime,
((ROUND(sc.time, 2) + (sc.miss * 5)) + ((sc.proc * 10) + (sc.saf * 10) + (sc.sog * 30)) - (sc.bthPoints)) cTime
FROM matches m
LEFT JOIN stages s ON s.match_id = m.id
LEFT JOIN scores sc ON sc.stage_id = s.id
WHERE m.matchuuid = 'E13A4C61-A2B8-48E2-BE1B-1FFB77CC5849'
GROUP BY sc.alias, s.stage_name
ORDER BY s.stage_name, zeroTime, cTime
) tv
) t
ORDER BY zeroTime, alias, stage_name;
Outputs:
+-----------+---------+-------+-----------------+-----+-----+--------+------+-----------------------+-------+----------+
| alias | catabbr | sh_dq | raw | sdq | dnf | rowNum | rank | stage_name | cTime | zeroTime |
+-----------+---------+-------+-----------------+-----+-----+--------+------+-----------------------+-------+----------+
| Back Bob | S | 0 | 52.9799995422 | 0 | 1 | 54 | 54 | Stage 1 | 92.98 | 0 |
+-----------+---------+-------+-----------------+-----+-----+--------+------+-----------------------+-------+----------+
| Back Bob | S | 0 | 43.9099998474 | 0 | 0 | 46 | 46 | Stage 2 | 48.91 | 0 |
+-----------+---------+-------+-----------------+-----+-----+--------+------+-----------------------+-------+----------+
| Ben Scal | ES | 0 | 26.9699993134 | 0 | 0 | 27 | 27 | Stage 1 | 31.97 | 0 |
+-----------+---------+-------+-----------------+-----+-----+--------+------+-----------------------+-------+----------+
| Ben Scal | ES | 0 | 32.8800010681 | 0 | 0 | 38 | 38 | Stage 2 | 42.88 | 0 |
+-----------+---------+-------+-----------------+-----+-----+--------+------+-----------------------+-------+----------+
Second query groups participants by name and sums their ranks for a final rank
SELECT alias, catabbr, SUM(cTime) fTime, SUM(rank) fRank, zeroTime, rank
FROM (
SELECT alias, catabbr, sh_dq, raw, sdq, dnf,
#rownum := IF(#stage != stage_name, 1, #rownum + 1) AS rowNum,
#rank := IF(#prevVal != cTime, #rownum, #rank) AS rank,
#stage := stage_name as stage_name,
#prevVal := cTime AS cTime,
id, zeroTime
FROM
(SELECT #rownum:= 0) rn,
(SELECT #rank:= 1) av,
(SELECT #stage:= '') sv,
(SELECT #prevVal:= 0) pv,
(SELECT s.stage_name stage_name, sc.alias alias, sc.catAbbr catabbr, sc.sh_dq sh_dq, sc.time raw, sc.id id, sc.sdq sdq, sc.dnf dnf,
IF(sc.time = "0", 1, 0) AS zeroTime,
((ROUND(sc.time, 2) + (sc.miss * 5)) + ((sc.proc * 10) + (sc.saf * 10) + (sc.sog * 30)) - (sc.bthPoints)) cTime
FROM matches m
LEFT JOIN stages s ON s.match_id = m.id
LEFT JOIN scores sc ON sc.stage_id = s.id
WHERE m.matchuuid = 'E13A4C61-A2B8-48E2-BE1B-1FFB77CC5849'
GROUP BY sc.alias, s.stage_name
ORDER BY s.stage_name, zeroTime, cTime
) tv
) t
GROUP BY alias
ORDER BY zeroTime, fRank, fTime;
Outputs:
+-----------+-----------+-----------+-------+-----------+
| alias | catabbr | fTime | fRank | zeroTime |
+-----------+-----------+-----------+-------+-----------+
| Back Bob | S | 141.89 | 100 | 0 |
+-----------+-----------+-----------+-------+-----------+
| Ben Scal | ES | 74.85 | 68 | 0 |
+-----------+-----------+-----------+-------+-----------+
The zeroTime column makes it so I can sort by times that are greater than 0, and correctly calculate the rank.
When I try to join them I get no results or anything. Is there a way to combine/join these two queries into one query so that they will output the following?
+-----------+-----------+-----------+-------+-----------+---------------+---------------+---------------+---------------+
| alias | catabbr | fTime | fRank | zeroTime | Stage 1 Time | Stage 1 Rank | Stage 2 Time | Stage 2 Rank |
+-----------+-----------+-----------+-------+-----------+---------------+---------------+---------------+---------------+
| Ben Scal | ES | 74.85 | 68 | 0 | 31.97 | 27 | 42.88 | 38 |
+-----------+-----------+-----------+-------+-----------+---------------+---------------+---------------+---------------+
| Back Bob | S | 141.89 | 100 | 0 | 92.98 | 54 | 48.91 | 46 |
+-----------+-----------+-----------+-------+-----------+---------------+---------------+---------------+---------------+
This will be going into a php page, so I can loop through and pull in what I need, the main thing I'm trying to do is join these two queries.
My structures and data are too big for a sqlfiddle.
Here is the structure and inserts for matches and stages: http://pastebin.com/YvZevd5j
Here is the structure and inserts for scores: http://pastebin.com/jLBYxMvc
One thing to note too, is that a game could have 1 or more stages and 1 or more particpants.
This Answer may not scale well as you would need to know the number of stages, but perhaps you can handle building it with a loop or something in you application. Here's a simplified version, you can modify it to work with your data.
SELECT
alias,
catAbbr,
sum(fTime) as fTime,
sum(fRank) as fRank,
sum(zeroTime) as zeroTime,
sum(Stage_1_Time) as stage_1_Time,
sum(Stage_1_Rank) as Stage_1_Rank,
sum(Stage_2_Time) as stage_2_Time,
sum(Stage_2_Rank) as Stage_2_Rank
FROM (
SELECT
alias,
catAbbr,
0 as fTime,
0 as fRank,
0 as zeroTime,
Stage_1_Time as stage_1_Time,
Stage_1_Rank as Stage_1_Rank,
0 as stage_2_Time,
0 as Stage_2_Rank
FROM (query that gets stage 1 info)
UNION
SELECT
alias,
catAbbr,
0 as fTime,
0 as fRank,
0 as zeroTime,
0 as stage_1_Time,
0 as Stage_1_Rank,
stage_2_Time as stage_2_Time,
Stage_2_Rank as Stage_2_Rank
FROM (query that gets stage 2 info)
UNION
SELECT
alias,
catAbbr,
fTime as fTime,
fRank as fRank,
zeroTime as zeroTime,
0 as stage_1_Time,
0 as Stage_1_Rank,
0 as stage_2_Time,
0 as Stage_2_Rank
FROM (query that gets averaged info)
) temp
GROUP BY alias, catAbbt