Selecting a value based on how another field was generated - mysql

I'm selecting some data;
select c.*,
coalesce(s.column1, ...),
coalesce(s.column2, ...),
FROM
(SELECT ...)
Basically, if s.column1 or s.column2 is null then I am putting in some logic to take the average of that column and use it instead.
I want to have another field so I can know weather or not that value was computing using the average or not - perhaps a boolean? Lets say the average for column1 was 120, the table would look like;
column1 column2 avg
54 10 0
200 40 0
120 180 1
499 160 0
This allows me to see that the third row was generated using the avg of all rows as it was initially null.
How could the logic for the avg column work?

Your question seems fairly moot to me because:
The AVG function ignores NULL values by default, so the average using the overall average for NULL slots is the same as leaving out those slots entirely, and
If you just want to mark the rows which had a NULL value, you can use a CASE expression
So, to get what you want, just use this:
SELECT
column1,
column2,
CASE WHEN column1 IS NULL THEN 1 ELSE 0 END AS avg
FROM yourTable;
And know that SELECT AVG(column1) FROM yourTable would return the same value whether NULL rows were omitted, or the overall average were used.

Related

subtract 2 values from the same table with UNION

I hope I can get some help here since i cannot think of anything else.
so i have 2 results that come from the same table which i want to see in one query... so i used UNION, all good until there, now i need the difference between these two results. Let's say i have the next result:
measure 30_days
old_purchases 342
new_purchases 54
i need a new row that gives me the difference between those two, something like that:
measure 30_days
old_purchases 342
new_purchases 54
difference 288
You can use conditional aggregation:
select measure, value
from t
union all
select 'difference',
sum(case when measure = 'old_purchases' then value
when measure = 'new_purchases' then - value
else 0
end)
from t;
If you actually want to add this to the table then use insert:
insert into t (measure, value)
select 'difference',
sum(case when measure = 'old_purchases' then value
when measure = 'new_purchases' then - value
else 0
end)
from t;

I need to create a query with a dynamic range of value in mysql

I have a table with a column called "Points", the value of this column have a min value of 0, and a max value of 100.000. I have to do an analysis per range of this column, so I wrote a query like this:
select case when Points between 0 and 5000 then '0 - 5000'
when Points between 5001 and 20000 then '50001 - 20000'
when Points > 20000 then '> 20000'
else 0 end RangeP
from Sales
The problem is that the customer wants to see for each 2.000 points from 0 to 100.000
If I wrote the query using the case, the query will get really big, so I'd like one way to get dynamically this range.
It is possible? Thank you very much
You may create a table which contains the ranges, and their text labels, and then join to it, e.g.
SELECT
s.Points,
COALESCE(r.label, 'range not found') AS range
FROM Sales s
LEFT JOIN
(
SELECT 0 AS start, 2000 AS end, '0 - 2000' AS label UNION ALL
SELECT 2001, 4000, '2001 - 4000' UNION ALL
...
SELECT 98000, 100000, '98000 - 100000'
) r
ON s.Points BETWEEN r.start AND r.end;
I have inlined the table of ranges, but you may create a formal table (or maybe a temp table) instead, and then replace the above subquery with just a reference to that table.

Finding LEAST/GREATEST values from combined COLUMNS, ignore 0 & NULL- MYSQL

I've got a dataset with a bunch of rows for monthly salary payments for each account. we have 6 columns for this -
Salary_1, Salary_2, Salary_3, Salary_4, Salary_5 and Salary_6.
Sometimes salaries 3, 4, 5, 6 and occasionally 2 aren't populated, sometimes none are populated because they're unemployed. In this case, we have 0 in the field.
What I need to do is combine all salaries and find the MAX and MIN from these columns ---
Select
Greatest(Salary_1, Salary_2, Salary_3, Salary_4, Salary_5, Salary_6) as MaxSal,
Least(COALESCE(Salary_1, Salary_2, Salary_3, Salary_4, Salary_5, Salary_6),0) as MinSal
from
(select
sal1 as Salary_1, Select sal2 as Salary_2, Sal3 as Salary_3, sal4 as Salary_4, Sal5 as Salary_5, Sal6 as Salary_6
from ....)a
The problem is, this is returning the correct value for Max Sal but 0.00 for Min, because it is the minimum value but won't let me ignore 0s, but in this case 0 is not a minimum salary value I want, I need the second lowest value here.
I've tried setting the original Sal1-Sal6 values to NULLIF 0 and it returns NULL for max and 0 for Min.
What else could I have a look at? the COALESCE combined with NULLIF has not worked for me which is what has been recommended on previous questions. Thanks!
Greatest and Least do not ignore nulls like aggregation functions do; you'll need to do something to avoid them. One option is something like this:
Greatest(IFNULL(Salary_1 ,0), ...)
Least(
CASE WHEN Salary_1 IS NULL OR Salary_1 = 0 THEN /*some huge value*/ ELSE Salary_1 END
, CASE WHEN Salary_2
....)
This might be simplest to unpivot and aggregate the data:
select id, max(salary), min(salary)
from ((select id, salary_1 as salary from t) union all
(select id, salary_2 as salary from t) union all
. . .
) t
group by id;
This is definitely more expensive than a giant case expression. On the other hand, it is less prone to error.
The real suggestion is to fix your data model. Trying to store an array in multiple columns is generally a sign of a poor data model. The more appropriate method would have one row per salary rather than putting them in separate columns.

Preference for rows where field is different from "x"

I want to query my database so that I don't order by or order by desc but instead seek rows when a given field is different from 0 first and only after get others ordered by if there are no rows with field != "0". What is the best way to accomplish this or even is it possible to do so?
This is supposing the field can have values from -100 to 0 to 100
With example:
Considering LIMIT equals 3 and field in consideration is field1
field1
10
0
-20
-40
Another set:
field1
0
0
-100
50
In the first set, the results extracted will be 10, -20 and -40 (by any order), while in the second set they should be -100, 50 and 0 (any of the zeros, by any order).
First I'd like to check whether there exists != 0 in the database and if true extract those and only after 0's until I fill the LIMIT
something like this?
its not clear AT ALL what you want so I'm only guessing here.
I want to query my database so that I don't order by or order by desc but instead seek rows when a given field is different from 0
seek rows to me means find all rows that are not 0.
only after get others ordered by if there are no rows with field != "0".
to me that means order the rows in a sub query where the field is not 0.
with that in mind maybe this query?
SELECT * FROM table t
WHERE field IN (
SELECT field
from table t
WHERE field <> 0
ORDER BY field
)
without data It's not clear what you want. but i think something like this is what you were asking.. you could also try
SELECT * FROM table t
WHERE field <> 0
ORDER BY field

Comparing 2 Columns in same table

I need to compare 2 columns in a table and give 3 things:
Count of rows checked (Total Rows that were checked)
Count of rows matching (Rows in which the 2 columns matched)
Count of rows different (Rows in which the 2 columns differed)
I've been able to get just rows matching using a join on itself, but I'm unsure how to get the others all at once. The importance of getting all of the information at the same time is because this is a very active table and the data changes with great frequency.
I cannot post the table schema as there is a lot of data in it that is irrelevant to this issue. The columns in question are both int(11) unsigned NOT NULL DEFAULT '0'. For purposes of this, I'll call them mask and mask_alt.
select
count(*) as rows_checked,
sum(col = col2) as rows_matching,
sum(col != col2) as rows_different
from table
Note the elegant use of sum(condition).
This works because in mysql true is 1 and false is 0. Summing these counts the number of times the condition is true. It's much more elegant than case when condition then 1 else 0 end, which is the SQL equivalent of coding if (condition) return true else return false; instead of simply return condition;.
Assuming you mean you want to count the rows where col1 is or is not equal to col2, you can use an aggregate SUM() coupled with CASE:
SELECT
COUNT(*) AS total,
SUM(CASE WHEN col = col2 THEN 1 ELSE 0 END )AS matching,
SUM(CASE WHEN col <> col2 THEN 1 ELSE 0 END) AS non_matching
FROM table
It may be more efficient to get the total COUNT(*) in a subquery though, and use that value to subtract the matching to get the non-matching, if the above is not performant enough.
SELECT
total,
matching,
total - matching AS non_matching
FROM
(
SELECT
COUNT(*) AS total,
SUM(CASE WHEN col = col2 THEN 1 ELSE 0 END )AS matching
FROM table
) sumtbl