MySQL greatest value in row? - php

I'm using MySQL with PHP. This is like my table: (I'm using 3 values, but there are more)
id | 1 | 2 | 3
---+---+---+----
1 | 3 |12 |-29
2 | 5 |8 |8
3 | 99|7 |NULL
I need to get the greatest value's column name in a certain row. It should get:
id | maxcol
---+-------
1 | 2
2 | 2
3 | 1
Are there any queries that will do this? I've been trying, but I can't get it to work right.

Are you looking for something like the GREATEST function? For example:
SELECT id, GREATEST(col1, col2, col3)
FROM tbl
WHERE ...
Combine it with a CASE statement to get column names:
SELECT id, CASE GREATEST(COALESCE(`1`, -2147483646), COALESCE(`2`, -2147483646), COALESCE(`3`, -2147483646))
WHEN `1` THEN 1
WHEN `2` THEN 2
WHEN `3` THEN 3
ELSE 0
END AS maxcol
FROM tbl
WHERE ...
It's not pretty. You'd do better to follow Bill Karwin's suggestion and normalize, or simply take care of this in PHP.
function findcol($cmp, $arr, $cols=Null) {
if (is_null($cols)) {
$cols = array_keys($arr);
}
$name = array_shift($cols);
foreach ($cols as $col) {
if (call_user_func($cmp, $arr[$name], $arr[$col])) {
$name = $col;
}
}
return $name;
}
function maxcol($arr, $cols=Null) {
return findcol(create_function('$a, $b', 'return $a < $b;'), $arr, $cols);
}

This is a great example of the way normalization helps make query design easier. In First Normal Form, you would create another table so all the values would be in one column, on separate rows.
Since you have used repeating groups to store your values across three columns, you can find the column with the greatest value this way:
SELECT id, IF(col1>col2 AND col1>col3, 'col1', IF(col2>col3, 'col2', 'col3'))
AS column_with_greatest_value
FROM mytable;

The short answer is that there is no simple means to do this via a query. You would need to transpose your data and then determine the largest value that way. So something like:
Select Id, ColumnName, Value
From (
Select '1' As ColumnName, Id, [1] As Value
From Table
Union All
Select '2', Id, [2]
From Table
Union All
Select '3', Id, [3]
From Table
) As Z
Where Exists(
Select 1
From (
Select '1' As ColumnName, Id, [1] As Value
From Table
Union All
Select '2', Id, [2]
From Table
Union All
Select '3', Id, [3]
From Table
) As Z2
Where Z2.Id = Z.Id
Group By Z2.Id
Having Max(Z2.Value) = Z.Value
)
Order By Id
This solution depends on a fixed set of columns where you basically name the columns in the UNION ALL queries. In addition, if you have two columns with identical values for the same Id, you will get duplicate rows.

This query will return the max value regardless of NULLs
SELECT MAX(value)
FROM
(SELECT 1 column_no, col1 value
FROM anotherunamedtable
UNION ALL
SELECT 2, col2
FROM anotherunamedtable
UNION ALL
SELECT 3, col3
FROM anotherunamedtable) t
If you really need the column number then
SELECT id,
(SELECT column_no
FROM
(SELECT 1 column_no, col1 value
FROM anotherunamedtable
WHERE id = t.id
UNION ALL
SELECT 2, col2
FROM anotherunamedtable
WHERE id = t.id
UNION ALL
SELECT 3, col3
FROM anotherunamedtable
WHERE id = t.id) s
ORDER BY max_value DESC
LIMIT 1)) as column_no
FROM anotherunamedtable t
But I think that the last query might perform exceptionally horrible.
(Queries are untested)

In the php side, you could do something like this:
foreach ($rows as $key => $row) {
$bestCol = $best = -99999;
foreach ($row as $col => $value) {
if ($col == 'id') continue; // skip ID column
if ($value > $best) {
$bestcol = $col;
$best = $value;
}
}
$rows[$key]['best'] = $bestCol;
}
Or something similar...

Forests and trees, here's a trivial and fastest solution (providing I didn't fumble); the expression simply looks for the largest column in the row
SELECT id,
CASE COALESCE(col1, -2147483648) >= COALESCE(col2, -2147483648)
WHEN
CASE COALESCE(col2, -2147483648) >= COALESCE(col3, -2147483648)
WHEN true THEN 1
ELSE
CASE COALESCE(col1, -2147483648) >= COALESCE(col3, -2147483648)
WHEN true THEN 1
ELSE 3
END
END
ELSE
CASE COALESCE(col2, -2147483648) >= COALESCE(col3, -2147483648)
WHEN true 2
ELSE 3
END
END
FROM table t
a version with IF() would maybe be more readable, but the above should perform a bit better
To deal with NULLS an INT value with minimum of -2147483648 was assumed, the expression could be rewritten to deal explicitly with nulls but would have to branch into 8 different cases and is left as an exercise for the OP.

Related

MYSQL: List all column names where their values = "Yes"

I have a table like so:
User_Id Column1 Column2 Column3
1 Yes No Yes
2
I want to use mysql query to list all the column names (there are more than 3) which match the User_Id '1' and have a value of 'Yes'.
I get an error:
Trying to get property 'num_rows' of non-object
Here is what I have tried:
<?php $myStats = $mysqli->query("SELECT COLUMN_NAME FROM user_services.columns WHERE myColumn = 'Yes'");
if ($myStats->num_rows > 0) {
// output data of each row
while($row = $myStats->fetch_assoc()) {
$rows[] = $row; }
return $rows; ?>
Please can someone show me where I am going wrong?
Thanks in advance.
The CONCAT_WS function comes in handy here:
SELECT CONCAT_WS(',', IF(Column1='Yes', 'Column1', NULL),
IF(Column2='Yes', 'Column2', NULL),
IF(Column3='Yes', 'Column3', NULL)) AS columns
FROM user_services.columns
WHERE User_Id = 1;
If you have more than 3 columns, then you may add more terms to above CONCAT_WS call. Your problem mainly seems to be a SQL one, so I won't add any PHP code.
Note that your design might be better off if your column strings were spread across rows, rather than columns. For instance, consider the following alternative:
User_Id | number | val
1 | 1 | Yes
1 | 2 | No
1 | 3 | Yes
Then, if you wanted all column numbers which were yes for user 1, you could simply do:
SELECT
User_Id,
GROUP_CONCAT(number ORDER BY number) columns
FROM yourTable
WHERE
User_Id = 1
GROUP BY
User_Id;

How to count mutiple columns occurrencies in whole table

I have 5 columns that has data which are ids from other table
how do I count how many times each number appears in whole table, in all records.
I want to count paslauga1, paslauga2, paslauga3, paslauga4, paslauga5 data occurrencies.
For example there is 2 records that has 4 times 1 id 2 times 2 id 1 time id 3
So I want it to output
1 has appeared 4 times
2 has appeared 1 times
3 has appeared 2 times
and if more ids would be there 4,5,6 etc it would display them too.
I need to count this so I can use it on php code, maybe theres easier way to do so in php?
Use a simple loop in PHP that counts the numbers in an array.
$counts = array();
$result = $pdo->query("SELECT paslauga1, paslauga2, paslauga3, paslauga4, paslauga5 FROM yourTable");
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
foreach ($row as $col) {
if ($col !=== null) {
if (isset($counts[$col])) {
$counts[$col]++;
} else {
$counts[$col] = 1;
}
}
}
}
ksort($counts);
foreach ($counts as $id => $c) {
echo "$id has appeared $c times<br>";
}
SELECT paslauga, count(paslauga)
FROM (
SELECT paslauga1 as paslauga FROM yourTable
UNION ALL
SELECT paslauga2 as paslauga FROM yourTable
UNION ALL
SELECT paslauga3 as paslauga FROM yourTable
UNION ALL
SELECT paslauga4 as paslauga FROM yourTable
UNION ALL
SELECT paslauga5 as paslauga FROM yourTable
) T
GROUP BY paslauga

Filtering SQL Results with Tags in a Junction Table [duplicate]

Lets consider the following table-
ID Score
1 95
2 100
3 88
4 100
5 73
I am a total SQL noob but how do I return the Scores featuring both IDs 2 and 4?
So it should return 100 since its featured in both ID 2 and 4
This is an example of a "sets-within-sets" query. I recommend aggregation with the having clause, because it is the most flexible approach.
select score
from t
group by score
having sum(id = 2) > 0 and -- has id = 2
sum(id = 4) > 0 -- has id = 4
What this is doing is aggregating by score. Then the first part of the having clause (sum(id = 2)) is counting up how many "2"s there are per score. The second is counting up how many "4"s. Only scores that have at a "2" and "4" are returned.
SELECT score
FROM t
WHERE id in (2, 4)
HAVING COUNT(*) = 2 /* replace this with the number of IDs */
This selects the rows with ID 2 and 4. The HAVING clause then ensures that we found both rows; if either is missing, the count will be less than 2.
This assumes that id is a unique column.
select Score
from tbl a
where a.ID = 2 -- based off Score with ID = 2
--include Score only if it exists with ID 6 also
and exists (
select 1
from tbl b
where b.Score = a.Score and b.ID = 6
)
-- optional? ignore Score that exists with other ids as well
and not exists (
select 1
from tbl c
where c.Score = a.Score and c.ID not in (2, 6)
)

Selecting a distinct value and the sum of times it shows up in a column

I have an existing table with millions of entries (growing) that consists of:
userid|name|etc...
1 frank ...
1 frank ...
2 joe ...
5 sam ...
1 franky ...
What I need to do is return a table of:
place|name|total
1 franky 3
2 sam 1
3 joe 1
Where total is the SUM(userid = the distinct userid).
Currently I'm doing a query to SELECT DISTINCT userid from table and then foreach returned value in php, I'm doing another query to return the name and sum(userid = userid).
As you can assume, this is very taxing and takes a long time now with all of the values. Is there any way to speed this up by doing 1 query?
i think you need
SELECT #a:=#a+1 AS `place`, name, COUNT(userid) AS `total`
FROM `your_table`, (SELECT #a:= 0) AS a
GROUP BY userid
SELECT userid, COUNT(*)
FROM some_table
GROUP BY userid

Ordering by a prototype

There are 6 possible keys in a MySQL field. Lets call them types. Through PHP, I have defined an array, that is called $order, and arranges these types in order I want them to appear.
There is a table, articles, which has a field articles.type . Any article can have 0-6 types added to it. Now, what I want to do, is grab all of the articles, and order them from the prototype. What is the best way to do this? Can this be done in MySQL, since I suppose that would be faster? And if not, how can it be done in PHP?
Example:
Table:
id articleId type
1 3 type1
2 3 type2
3 3 type3
4 3 type4
5 4 type5
6 4 type6
7 5 type5
8 7 type1
9 7 type5
Order:
$order=array('type1','type2','type3','type4','type5','type6');
How do I fetch the results ordered by my $order variable?
You'd need to massage that array into a mysql-style if/case statement:
$order_by = "ORDER BY CASE";
$pos = 1;
foreach ($order as $clause) {
$order_by .= " CASE `type`='$clause' THEN " . $pos++;
}
$order_by .= " ELSE " . $pos++;
which would generate something like
ORDER BY CASE
WHEN `type`='type1' THEN 1
WHEN 'type`='type2' THEN 2
...
ELSE n
Can this be done in MySQL, since I suppose that would be faster?
Only if you allow MySQL to use an index
You can create a temp table:
$query = "CREATE TABLE IF NOT EXISTS test (
sort INT NOT NULL AUTO_INCREMENT,
`type` VARCHAR(20) NOT NULL,
PRIMARY KEY (sort),
KEY (`type`, sort)
ENGINE=MEMORY (SELECT 1,'other' <<-- see query below.
UNION SELECT 2,'type1' <<-- build this part using
UNION SELECT 3,'type2' <<-- Marc B's code.
UNION SELECT 4,'type3'
UNION SELECT 5,'type4'
UNION SELECT 6,'type5'
UNION SELECT 7,'type6' ";
Run this query.
Now you can link against this query using a join and use test.sort as your sortkey:
SELECT t1.id, t1.article_id, COALESCE(t.`type`,'other') as sort_type
FROM table1 t1
LEFT JOIN test t ON (t1.`type` = t.`type`)
WHERE ....
ORDER BY t.sort;
This query will be fully indexed and run as fast as possible.

Categories