I have a MySQL table from which I want to select:
1) Either "most occurring" value, if there is any prevailing
2) Or "average" value, if there is no most occurring value.
Example table 1:
value
1
2
3
4
All values are occurred equally, therefore I want to take AVG(`value`)
Example table 2:
value
1
2
2
3
Value 2 prevails, therefore I want to select the value 2.
What mysql query would do this?
Starting from Gordon's answer I tested and corrected the SQL query in SQL Fiddle:
SELECT IF(t4.numcnts = 1, t1.avgvalue, t2.topvalue) AS result
FROM (select avg(value) as avgvalue from test) t1
CROSS JOIN (select value as topvalue from test group by value order by count(*) desc limit 1) t2
CROSS JOIN join (select count(distinct cnt) as numcnts from
(select count(*) as cnt from test group by value) t3) t4
Here is the Fiddle with the two test tables (switch out test2 for test to see the result when a particular value prevails): http://sqlfiddle.com/#!2/76914/3
My changes were to use an IF instead of a CASEstatement in the SELECTclause and to add the necessary table aliases for the subselects.
The following approach calculates both values and then chooses between them:
select (case when numcnts = 1 then avgvalue else topvalue end)
from (select avg(value) as avgvalue from t) cross join
(select value as topvalue from t group by value order by count(*) desc limit 1) cross join
(select count(distinct cnt) as numcnts from (select count(*) as cnt from t group by value))
Note: if you have ties for the top, but other values as well, then an arbitrary value is returned. You don't specify what to do in this case.
Also, the SQL is untested, so it might have syntax errors.
Related
Id like to run a query only if a value in the last row is correct. In my exemple if the value in ColumnA is 1 on the last row then i want to run MyQuery. But if the value is not 1 stop there and do nothing.
i've try with case and count(*) and also with If exists. but i keep getting error
SELECT CASE WHEN ((SELECT COUNT(*) FROM
(Select a.* from table as a order by a.index desc limit 1) as b
where b.ColumnA = 1)) > 0 )
THEN (MyQuery)
END
i've also try with if exists but it doesn'work either
if exists Select b.* from (Select a.* from table as a order by a.index desc limit 1) where b.ColumnA = 1
begin
(MyQuery)
end
can you point me what wrong in those query or maybee there's a better way to achive this.
EDIT. This query will be run on a trigger after each insert in that table the goal is to avoid running MyQuery on row that dont required it. MyQuery is a bit slow and most row dont required it to run.
I think we can rephrase your logic here to make it work as you want:
WITH cte AS (
SELECT ColumnA, ROW_NUMBER() OVER (ORDER BY index DESC) rn
FROM yourTable
)
(your query here)
WHERE (SELECT ColumnA FROM cte WHERE rn = 1) = 1;
The WHERE clause above would return either true or false, and would apply to all records in the potential result set from your query. That is, if the ColumnA value from the "last" record were 1, then you would get back the entire result set, otherwise it would be empty set.
Assuming your version of MariaDB supports neither ROW_NUMBER nor CTEs, then use:
(your query here)
WHERE (SELECT ColumnA FROM yourTable ORDER BY index DESC LIMIT 1) = 1;
It depends on what your query is.
INSERT ...
SELECT ... WHERE ... -- this could lead to zero rows being inserted
DELETE ...
WHERE NOT EXISTS ( SELECT ... ) -- this could lead to zero rows being deleted
UPDATE t1 JOIN t2 ... -- the JOIN may cause no rows to be updated
Note:
(Select a.* from table as a order by a.index desc limit 1) as b
where b.ColumnA = 1)) > 0 )
can be simplified (and sped up) to
( ( SELECT ColumnA FROM table ORDER BY index DESC LIMIT 1 ) = 1 )
Note that that is a true/false "expression", so it can be used in various places.
I have the answer below that worked fine for me:
now instead of selecting distinct or group by , I want to select all that records ext_no having value zero only.
How to modify this answer? I tried using order by but it's showing an error. Any help will be appreciated. Thanks in advance.
SELECT ext_no, MAX(value)
FROM test
GROUP BY ext_no
HAVING COUNT(*) > 2 AND
COUNT(*) = COUNT(CASE value WHEN 0 THEN 1 END)
There are a couple of ways you can achieve this. Using a couple of correlated subqueries in the WHERE clause or by JOINing to a table containing MAX(value) and COUNT(*) values for each value of ext_no:
Query 1
SELECT ext_no
FROM test t1
WHERE NOT EXISTS (SELECT *
FROM test t2
WHERE t2.ext_no = t1.ext_no AND value != 0)
AND (SELECT COUNT(*)
FROM test t3
WHERE t3.ext_no = t1.ext_no) > 2
The first subquery checks that this ext_no has no value other than 0. The second checks that there are more than 2 copies of this ext_no.
Query 2
In this query we apply all the conditions (no non-zero values of value and more than 2 copies of ext_no) in the JOIN condition.
SELECT t1.ext_no
FROM test t1
JOIN (SELECT ext_no, MAX(value) AS max_value, COUNT(*) AS count
FROM test
GROUP BY ext_no) t2
ON t2.ext_no = t1.ext_no AND t2.max_value = 0 AND t2.count > 2
Output (for both queries)
ext_no
12133
12133
12133
11505
11505
11505
11505
Demo on SQLFiddle
I think there is no question like this.
I need to group rows by n records and get some values of this group.
I think is better to explain with a graphic example:
Is possible to do a query like this? if not my solution will be make an script to create another table with this but I donĀ“t like duplicate data at all.
Thanks!!!
set #counter=-1;
select xgroup,max(x) as mx, max(y) as my, avg(value3) as v3,
from
(
select (#counter := #counter +1) as counter,
#counter div 5 as xgroup,
currency, datetime, value1, value2,
case mod(#counter,5) when 0 then value1 else 00 end as x,
case mod(#counter,5) when 4 then value2 else 00 end as y,
mod(#counter,5) as xxx
FROM findata
) name1
group by xgroup;
#jms has the right approach, but you have to be very careful when using variables:
You should not assign a variable in one expression and then reference it in another in the same select.
To work in the most recent versions of MySQL, I would suggest ordering the data in a subquery.
In addition, there are some other values that you need:
select min(col1), min(col2),
max(case when mod(rn, 5) = 0 then col3 end),
max(col4), min(col5),
max(case when mod(rn, 5) or rn = #rn then col6 end),
max(case when mod(rn, 5) or rn = #rn then col7 end)
from (select (#rn := #rn + 1) as rn, t.*
from (select t.*
from t
order by col1, col2
) t cross join
(select #rn := -1) params
) t
group by (#rn div 5);
Note the logic is a bit arcane for the last values -- this is to take into account the final group that might not have exactly 5 rows.
You need a column that looks like(assuming you want to group every 5 rows)
dummy_table
1
1
1
1
1
2
2
2
2
2
...
You can do this by using generate_series() if you are using postgre sql by using
select t1 from (select generate_series(1,x)) t1, (select generate_series(1,5)) t2;
where you can replace x by (total rows/5) i.e. for 100 rows, x = 20. If you are using any other SQL platform, you can just work on creating this dummy table accordingly.
Once you get this dummy_table, join it with your table on row_number of your table with t1 column of dummy_table(not row_number of dummy_table). Syntax for accessing row number should be straightforward.
After the join, group by this t1 column and do the required aggregation. To do this in a single query, you can do the above in an inner query and do aggregation outside it. Hope this makes sense.
Ok, thanks you all guys for your answers, thanks to it I found the simple solution.
I simply add an autoincrement column, and then I can group results by integer division by 5.
And with this query:
SELECT id,
symbol,
datetime,
open,
MAX(high),
MIN(low),
SUBSTRING_INDEX( GROUP_CONCAT(CAST(close AS CHAR) ORDER BY datetime DESC), ',', 1 ) AS close
FROM `table`
GROUP BY (id-1) DIV 5
And the resulting is:
Thanks!
A solution is to introduce some field for grouping rows for aggregative operations.
It can be reached by introducing a user-variable and assigning values that will allow to group rows as required. For example, it can be a row counter divided by grouping chuck size and rounded to nearest upper ceil number:
SET #counter=0;
SELECT CEIL((#counter:=#counter+1)/5) AS chunk, MAX(high), MIN(low) FROM `table` GROUP BY chunk;
We read values from a set of sensors, occasionally a reading or two is lost for a particular sensor , so now and again I run a query to see if all sensors have the same record count.
GROUP BY sensor_id HAVING COUNT(*) != xxx;
So I run a query once to visually get a value of xxx and then run it again to see if any vary.
But is there any clever way of doing this automatically in a single query?
You could do:
HAVING COUNT(*) != (SELECT MAX(count) FROM (
SELECT COUNT(*) AS count FROM my_table GROUP BY sensor_id
) t)
Or else group again by the count in each group (and ignore the first result):
SELECT count, GROUP_CONCAT(sensor_id) AS sensors
FROM (
SELECT sensor_id, COUNT(*) AS count FROM my_table GROUP BY sensor_id
) t
GROUP BY count
ORDER BY count DESC
LIMIT 1, 18446744073709551615
SELECT sensor_id,COUNT(*) AS count
FROM table
GROUP BY sensor_id
ORDER BY count
Will show a list of the sensor_id along with a count of all the records it has, you can then manually check to see if any vary.
SELECT * FROM (
SELECT sensor_id,COUNT(*) AS count
FROM table
GROUP BY sensor_id
) AS t1
GROUP BY count
Will show all the counts that vary, but the group by will lose information about which sensor_ids have which counts.
---EDIT---
Taken a bit from both mine and eggyal's answer and created this, for the count that is most frequent I call the id default, and then for any values that stand out I have given them separate rows. This way you maintain the readability of a table if you have many results Multi Row, but also have a simple one row column if all counts are the same One Row. If however you are happy with the concocted strings then go with eggyal's answer.
Might be a bit over the top but here goes:
select 'default' as id,t5.c1 as count from(
select id,count(*) as c1 from your_table group by id having count(*)=
(select t4.count from
(
select max(t3.count2) as max,t3.count as count from
(
select count(*) as count2,t2.count from
(
SELECT id,COUNT(*) AS count
FROM your_table
GROUP BY id
) as t2
GROUP BY count
) as t3
) as t4)) as t5 group by count
union all
select t5.id as id,t5.c1 as count from(
select id,count(*) as c1 from your_table group by id having count(*)<>
(select t4.count from
(
select max(t3.count2) as max,t3.count as count from
(
select count(*) as count2,t2.count from
(
SELECT id,COUNT(*) AS count
FROM your_table
GROUP BY id
) as t2
GROUP BY count
) as t3
) as t4)) as t5
select sum(value) as 'Value',max(value)
from table_name where sum(value)=max(sum(value)) group by id_name;
The error is: Invalid use of group function (ErrorNr. 1111)
Any idea?
Thanks.
Can you maybe try
SELECT Value, MXValue
FROM (
select sum(value) as 'Value',max(value) MXValue
from table_name
group by id_name
) as t1
order by value desc
LIMIT 0,1
From MySQL Forums :: General :: selecting MAX(SUM())
Or you could try something like
SELECT id_name,
Value
FROM (
select id_name,sum(value) as 'Value'
from table_name
group by id_name
) t
WHERE Value = (
SELECT TOP 1 SUM(Value) Mx
FROM table_name
GROUP BY id_name
ORDER BY SUM(Value) DESC
)
Or even with an Inner join
SELECT id_name,
Value
FROM (
select id_name,sum(value) as Value
from table_name
group by id_name
) t INNER JOIN
(
SELECT TOP 1 SUM(Value) Mx
FROM table_name
GROUP BY id_name
ORDER BY SUM(Value) DESC
) m ON Value = Mx
The =max(sum(value)) part requires comparing the results of two grouped selects, not just one. (The max of the sum.)
Let's step back, though: What information are you actually trying to get? Because the sum of the values in the table is unique; there is no minimum or maximum (or, depending on your viewpoint, there is -- the value is its own minimum and maximum). You'd need to apply some further criteria in there for the results to be meaningful, and in doing so you'd probably need to be doing a join or a subselect with some criteria.