Mysql increment value on multiple cases to order by - mysql

I have a table with products, and by answering a set of questions (Could be 2 or could be six, it's not always the same) I want to get results. Each answer has rules to it like color = green or noise > 69.
Now I don't want to use these rules as a WHERE to refine my searchresults, but I want to increment a variable on CASE so I can ORDER on the amount of true conditions. I still get all values, but the ones that better suit my customers needs are on top.
I tried a lot already, something like:
SELECT a.*, b.*,COUNT(CASE WHEN (b.noise >= 1600) THEN 1 ELSE 0 END) as condition_true
But I cant get it to work with multiple CASES.
SAMPLE DATA:
Products table:
id | title
1 | washing machine X
2 | Washing Machine Y200
3 | Even cooler washing machine
Productinfo table
id | noise | color | locked | product_id
1 | 40 | white | 1 | 1
2 | 68 | green | 0 | 2
3 | 72 | green | 1 | 3
Possible rules I will use in the output table
b.noise > 42
b.color = "green"
b.locked = 1
I would love an output table like this
product_id | title | condition_true
3 | Even cooler washing machine | 3
2 | Washing Machine Y200 | 2
1 | washing machine X | 1

CASE only returns one value, and COUNT requires multiple rows. You could just add them.
SELECT a.*, b.*, (CASE WHEN b.noise >= 1600 THEN 1 ELSE 0 END) +
(CASE WHEN color = 'green' THEN 1 ELSE 0 END) AS relevance
FROM table1
ORDER BY relevance DESC
Or, abbreviated for MySQL:
SELECT a.*, b.*, (b.noise >= 1600) +
(color = 'green') AS relevance
FROM table1
ORDER BY relevance DESC

COUNT is not what you want, that works to aggregate results from multiple rows when using GROUP BY.
I would do this using a series of IF() statements, returning 1 or 0 based on the condition (you could also return a bigger number, for more important columns or something):
SELECT IF(b.noise >= 1600,1,0)+IF(b.ocl1 <= 3,1,0)+...;

Related

How to make a query to pivot/aggregate like this?

How would I query this data set:
SessionId | GameMode | Score
----------|----------|-------
1 | 1 | 100
1 | 1 | 90
1 | 2 | 20
1 | 2 | 15
1 | 3 | 5
1 | 3 | 5
2 | 1 | 90
2 | 1 | 80
2 | 2 | 15
2 | 2 | 15
2 | 3 | 2
2 | 3 | 4
to turn it into:
SessionId | GameMode1AvgScore | GameMode2AvgScore | GameMode3AvgScore
----------|--------------------|-------------------|-------------------
1 | 95 | 17.5 | 5
2 | 85 | 15 | 3
To clarify, I'm not asking how to group or aggregate rows, the core of my question is rather how do I pivot the GameMode rows into a column?
With MySQL, to return the specified resultset, we would need a query (SELECT statement) that returns four columns.
SELECT expr1 AS SessionId
, expr2 AS GameMode1AvgScore
, expr3 AS GameMode2AvgScore
, expr4 AS GameMode3AvgScore
FROM ...
GROUP BY SessionId
MySQL does not yet provide "PIVOT" (ala SQL Server and PostgreSQL) or "MODEL" (ala Oracle) features.
So in terms of achieving this result from a SQL SELECT statement, we are left with old-school conditional aggregation,
AVG(CASE WHEN t.GameMode = 1 THEN t.Score ELSE NULL END) AS GameMode1AvgScore
which seems to fall into the "how to group or aggregate rows" bucket you are not asking about. We can use a second SQL statement to help us build the SQL statement we need to run, but seems like that would still fall into the "how to group or aggregate rows". In fact, any SQL statement that produces the specified result is going to need to aggregate rows.
So with that tool excluded from our MySQL toolbelts, that leaves us with one answer: it is not possible to "pivot" GamerMode values into columns.
A statement to return the specified result might be like this:
SELECT t.sessionid AS SessionId
, AVG(CASE WHEN t.GameMode = 1 THEN t.Score ELSE NULL END) AS GameMode1AvgScore
, AVG(CASE WHEN t.GameMode = 2 THEN t.Score ELSE NULL END) AS GameMode2AvgScore
, AVG(CASE WHEN t.GameMode = 3 THEN t.Score ELSE NULL END) AS GameMode3AvgScore
-- ^ ^
FROM t
GROUP BY t.sessionid
ORDER BY t.sessionid
Note how expressions to return the average scores are the same pattern, we're just substutiting in a value of GameMode.
To help us write the query, we could run an initial query to get a distinct list of GameMode values, either from the same table
SELECT q.GameMode FROM t q GROUP BY q.GameMode ORDER BY q.GameMode
or we could query a "dimension" table of GameMode if that exists.
We cycle through the resultset (GameMode values) and generate the expression required to return the average score for each GameMode. And we can use that to construct the SQL statement.

Getting Corresponding Row Values to a GROUP BY with MIN

I've got data that's something like this:
+------------+---------+-------+
| Name | Time | Flag |
+------------+---------+-------+
| Bob | 401 | 1 |
| Bob | 204 | 0 |
| Dan | 402 | 1 |
| Dan | 210 | 0 |
| Jeff | 204 | 0 |
| Fred | 407 | 1 |
| Mike | 415 | 1 |
| Mike | 238 | 0 |
+------------+---------+-------+
I want to get each person's best time, but if the "flag" is set, their time should be divided by 2.
For example, Bob's best time would be 200.5
Now I can do a relatively simple query to get this data like this:
SELECT userid,
MIN(CASE WHEN flag = 1 THEN time / 2 ELSE time END) AS convertedTime,
time,
flag
FROM times t
GROUP BY userid
ORDER BY convertedtime ASC
The problem here is that is doesn't return the proper corresponding data for the time and flag, getting this data:
Bob 200.5 204 0
instead of the correct data
Bob 200.5 401 1
Of course I see the issue with the previous query and I've fixed it with this:
SELECT userid,
MIN(convertedtime) AS convertedTime,
(SELECT time
FROM times
WHERE MIN(convertedtime) = CASE
WHEN flag = 1 THEN time / 2
ELSE time
end
LIMIT 1) AS time,
(SELECT flag
FROM times
WHERE MIN(convertedtime) = CASE
WHEN flag = 1 THEN time / 2
ELSE time
end
LIMIT 1) AS flag
FROM (SELECT userid,
CASE
WHEN flag = 1 THEN time / 2
ELSE time
end AS convertedTime,
time,
flag
FROM times t) AS t
GROUP BY userid
ORDER BY convertedtime ASC
SQLFiddle
This does work, but I feel like there has to be a better and more efficient way of doing this. In my actual query, the part where I'm dividing the time by 2 is a much longer formula and I've got thousands of rows so it's very slow.
So the question is, is there a better/more efficient query for this?
Use a common technique from SQL select only rows with max value on a column but add the flag check when comparing the values in the ON condition.
SELECT t1.username, t2.convertedtime, t1.time, t1.flag
FROM times AS t1
JOIN (SELECT username,
MIN(IF(flag = 1, time/2, time) AS convertedtime
FROM times
GROUP BY username) AS t2
ON t1.username = t2.username
AND t1.time = IF(t1.flag = 1, t2.convertedtime * 2, t2.convertedtime)
This probably won't be very efficient -- I don't think it will be able to use an index for the conditional comparison.
This is what I ended up using (couldn't multiply by 2 to revert the number in this case due to my more complicated actual query having rounding in it), but Barmar did pretty much the same thing as I'm doing so I marked his as the answer.
SELECT times.userID, times2.convertedTime, times.time, times.flag
FROM times JOIN
(
SELECT userid,
MIN(CASE WHEN flag = 1 THEN time / 2 ELSE time END) AS convertedTime,
time,
flag
FROM times t
GROUP BY userid
ORDER BY convertedtime ASC
) as times2 ON times.userid = times2.userid AND
(
(times.time / 2 = times2.convertedTime AND times.flag = 1) OR
(times.time = times2.convertedTime AND times.flag = 0)
)

Mysql subquery for group and count records by specific field

I have this problem:
I have a car table, each car record has a state field, state field value can be:
1 = Enable or 2 = Disable
For example, in this case I need to show all cars grouped by color field
and counted by color, this is not problem really :)
Here the SQL statement :
SELECT
`id`,
`model_family`,
`color`,
COUNT(`color`) AS 'quantity',
`state`
FROM `auto`
WHERE `model_family` = 'Sedan'
GROUP BY `color`
+-----+--------------+------------+----------+---+
| id | model_family | color | quantity | state |
+-----+--------------+--------+----------+-------+
| 77 | Sedan | Red | 2 | 2 |
| 42 | Sedan | Blue | 3 | 2 |
| 97 | Sedan | Green | 5 | 1 |
+-----+--------------+--------+----------+-------+
Results show two Disabled records and one Enabled record.
Well, the questions is :
How can I can show the cars disabled and enabled but
For example, if for first grouped record if state is disabled (state = 2)
then quantity field will appear as "0" (because it not exists cars enabled)
else quantity = n
Something like this:
+-----+--------------+------------+----------+-------+
| id | model_family | color | quantity | state |
+-----+--------------+------------+----------+-------+
| 77 | Sedan | Red | 0 | 2 |
| 42 | Sedan | Blue | 0 | 2 |
| 97 | Sedan | Green | 5 | 1 |
+-----+--------------+------------+----------+-------+
Regads !
SELECT
`id`,
`model_family`,
`color`,
SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END) as enabled,
SUM(CASE WHEN state = 2 THEN 1 ELSE 0 END) as disabled,
`state`
FROM `auto`
WHERE `model_family` = 'Sedan'
GROUP BY `color`
You have to use case statements to group the items together. When it finds that the state is 1 you will need to sum together all of those records which is why I am doing the Then 1 Else 0.
This was a quick example of how to do it. I haven't tested to make sure it works, but it should.
If you want all disabled groups to be replaced with 0, you can just put a single case statement in your select clause. Something like this:
SELECT id, model_family, color, (CASE WHEN state = 1 THEN COUNT(*) ELSE 0 END) AS quantity, state
FROM auto
WHERE model_family = 'Sedan'
GROUP BY color
ORDER BY id;
It tested fine with your data on SQL Fiddle.
EDIT Note, since you're grouping by color, you can just use COUNT(*) instead of COUNT(color) because they are going to count the same thing.
EDIT 2 Also important to note that if a color has some enabled vehicles and some disabled vehicles, it still returns 0 because there is at least one disabled. If you want a count of the enabled ones, you can do something like this:
SELECT id, model_family, color, SUM(CASE WHEN state = 1 THEN 1 ELSE 0 END) AS quantityEnabled, state
FROM auto
WHERE model_family = 'Sedan'
GROUP BY color
ORDER BY id;
This fiddle has both examples.

Mysql query for counting winnings and losings

I have a table with some columns. One of the column registers if the row is true or false. E.g if you lost the game, this is set to 0 else 1.
What I want to count is the amount of 1 in row. The table looks like this:
+-----------+
|game_status|
|-----------|
|00000000 |
|-----------|
|00000000 |
|-----------|
|00000001 |
|-----------|
|00000001 |
|-----------|
|00000001 |
|-----------|
| ... |
+-----------+
So if you count it by hand the result would be:
starting from 0:
lost(0) -> 0 - 1 =
lost(0) -> -1 - 1 =
won(1) -> -2 + 1 =
won(1) -> -1 + 1 =
won(1) -> 0 + 1 =
Result = 1
So how do I get this result using mysql queries? I have tried using count but it counts all ones or zeros.
Thanks in advance.
I think I got a little bit confused when I accepted the answer and I do apologize for that. What I think I forgot to say is the result of the query should look like this:
+--------+------+
| 1 | -1 |
|--------|------|
| 2 | -2 |
|--------|------|
| 3 | -1 |
+---------------+
There the incremental column is the amount of games played...
You could count the wins and subtract the count of the loses.
select (select count(status) from table1 where status = 1) -
(select count(status) from table1 where status = 0)
try:
SELECT COUNT(DISTINCT CASE WHEN Column = 0 THEN 1 END) AS TotalZeros,
COUNT(DISTINCT CASE WHEN Column = 1 THEN 1 END) AS TotalOnes
FROM YourTable
Or much simpler way is if your column only holds either 0 or 1 just select count of either one of them and subtract it from the total rows which will give you the count of other.
Use SUM instead of COUNT. COUNT finds the number of items regardless of their value, SUM actually totals up the integer values.
Look here for reference
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
SELECT sum(CASE game_status WHEN 1 THEN 1 ELSE -1 end) FROM table_name;

mysql 3 queries on 2 tables in one

I have 2 tables that I need to query
**tbl_jobs**
jobid | description | someinfo
1 foo bar
2 fuu buu
**tbl_invlog**
idinv | jobid | type | ammount
1 1 add 100
2 1 rem 50
3 1 rem 15
4 1 add 8
5 2 add 42
the result should be to make a sum of the inventory "add" and "rem" and give a total of sum(add)-sum(rem) for each jobid, including the rest of the job information.
jobid | description | someinfo | amountadd | amountrem | totaladdrem
1 | foo | bar | 108 | 65 | 43
2 | fuu | buu | 42 | 0 | 42
i have made a quadruple select statement with select * from (select .... ) without using joins or other cool stuff. which is terribly slow. I am quite new to mysql.
I would be glad to an idea on how to solve this.
thanks in advance
This is a query that requires a join and conditional aggregation:
select j.jobid, j.description, j.someinfo,
sum(case when il."type" = 'add' then amount else 0 end) as AmountAdd,
sum(case when il."type" = 'rem' then amount else 0 end) as AmountRem,
(sum(case when il."type" = 'add' then amount else 0 end) -
sum(case when il."type" = 'rem' then amount else 0 end)
) as totaladdrem
from tbl_jobs j left outer join
tbl_invlog il
on j.jobid = il.jobid
group by j.jobid, j.description, j.someinfo;
Note some things. First, the tables have table aliases, defined in the from clause. This allows you to say which table the columns come from. Second, the table aliases are always used for all columns in the query.
MySQL would allow you to just do group by j.jobid, using a feature called "hidden columns". I think this is a bad habit (except in a few cases), so this aggregates by all the columns in the jobs table.
The conditional aggregation is done by putting a condition in the sum() statement.