How to get min and max with filter efficiently? - sql

I have DB like this (it's actually a much bigger than this):
value
-------
10
20
50
600
70
800
I want to get min and max values, which satisfies some conditions (where).
I wrote these selects:
select value
from records
where value >= 50
order by value asc
limit 1
select value
from records
where value <= 600
order by value desc
limit 1
But can I do it more efficiently and in one select?

You can combine both conditions using and in WHERE clause and use min() and max() functions on value.
select max(value) as Maximum, min(value) as Minimum
from records
where value >= 50
and value <= 600

A more general approach uses conditional aggregation:
select max(value) filter (where value >= 50),
min(value) filter (where value <= 600)
from records ;
Filtering in the WHERE clause happens to work for the limits in your problem. However, it does not generalize to more than two values. And is cumbersome even for other pairs.

Related

Return column with the second largest date in the dataset

I have a simple query that returns the dataset and a measure that represents a column with the highest date found.
select *, max(date) over () as date_max
from table
I need to increment this query and insert a new column that returns the second highest date found (it will still contain that column with the highest date). Any idea how to do this?
You can just query for the second largest like
select max(date) from table where date < (select max(date) from table)
You can use this in a subquery in the main query for example. Alternatively, you can get the max date first, and then use it to get the second largest value, also using a window function like you did, like so:
with top1 as
(
select i, max(i) over () as largest
from d
)
select i, largest
-- ignore the largest value when aggregating
, max(case when i = largest then null else i end) over () as second_largest
from top1
Keep in mind both of them will return the actual second largest value, duplicates excluded (so 100, 100, 99 would return 99 as the second largest).
live demo on dbfiddle

How remove rows or filter rows with some identical columns values by some criteria?

I have a table which I filter and sort with this query
select * from XXXX where Segment='Gewerbe' and Division='Strom' and consumption >= 1000 and consumption < 2000 and rank<= 5 and market_id='39a2e05fd43300c998558ef56bca18e2' order by consumption ,rank
The result set contains basically three groups of results which are grouped MARKET_ID and RANK. Each subresult differs by RANK (1..N).
The difficult part: I am interested only in the subresults with the highest RANK In this case I need each row with RANK=5. So I want to eliminate the rows with RANK=1..4. Note that highest RANK for each subresult might be smaller than 5.
Result table
I think the following would work too:
select market_id, consumption, max(costs_netto), max(rank) from XXX where Segment='Gewerbe' and Division='Strom' and consumption >= 1000 and consumption < 2000 and rank<= 5 and market_id='39a2e05fd43300c998558ef56bca18e2'[enter image description here][1] group by market_id, consumption order by market_id, consumption
Result with grouping

SQL AVG() function returning incorrect values

I want to use the AVG function in sql to return a working average for some values (ie based on the last week not an overall average). I have two values I am calculating, weight and restingHR (heart rate). I have the following sql statements for each:
SELECT AVG( weight ) AS average
FROM stats
WHERE userid='$userid'
ORDER BY date DESC LIMIT 7
SELECT AVG( restingHR ) AS average
FROM stats
WHERE userid='$userid'
ORDER BY date DESC LIMIT 7
The value I get for weight is 82.56 but it should be 83.35
This is not a massive error and I'm rounding it when I use it so its not too big a deal.
However for restingHR I get 45.96 when it should be 57.57 which is a massive difference.
I don't understand why this is going so wrong. Any help is much appreciated.
Thanks
Use a subquery to separate selecting the rows from computing the average:
SELECT AVG(weight) average
FROM (SELECT weight
FROM stats
WHERE userid = '$userid'
ORDER BY date DESC
LIMIT 7) subq
It seems you want to filter your data with ORDER BY date DESC LIMIT 7, but you have to consider, that the ORDER BY clause takes effect after everything else is done. So your AVG() function considers all values of restingHR from your $userId, not just the 7 latest.
To overcome this...okay, Barmar just posted a query.

SQL Difference between using two dates

My database has a table called 'clientordermas'. Two columns of that table are as follows.
execid and filledqty.
Three records of those two fields are as follows.
E02011/03/12-05:57_24384 : 1000
E02011/03/12-05:57_24384 : 800
E02011/03/09-05:57_24384 : 600
What i need to do is get the filledqty diffrence btween latest date and 1 before latest date which is 400(1000-400).
I have extracted the date from the execid as follows:
(SUBSTR (execid, 3, 10)
I tried so hard but but I was unable to write the sql query to get 400. Can someone please help me to do this???
P.S I need to select maximum filled quantity from the same date. That is 1000 not, 800.
You can use window functions to access "nearby" rows, so if you first clean up the data in a subquery and then use window functions to access the next row, you should get the right results. But unless you have an index on substr(execid, 3, 10), this is going to be be slow.
WITH datevalues AS
(
SELECT max(filledqty) maxfilledqty, substr(execid, 3, 10) execiddate
FROM clientordermas
GROUP BY substr(execid, 3, 10)
)
SELECT
execiddate,
maxfilledqty -
last_value(maxfilledqty) over(ORDER BY execiddate DESC ROWS BETWEEN 0 PRECEDING AND 1 FOLLOWING)
FROM datevalues
ORDER BY execiddate DESC;
WITH maxqtys AS (
SELECT substr(a.execid,3,10) AS date, MAX(a.filledqty) AS maxqty
FROM clientordermas a
GROUP BY substr(a.execid,3,10)
)
SELECT a.maxqty-b.maxqty
FROM maxqtys a, maxqtys b
WHERE a.date <> b.date AND ROWNUM=1
ORDER BY a.date DESC, b.date DESC
This first creates a subquery (maxqty) which contains the max filledqty for each unique date, then cross join this subquery to itself, excluding the same rows. This results in a table containing pairs of dates (excluding the same dates).
Sort these pairs by date descending, and the top row till contain the last and 2nd-to-last date, with the appropriate quantities.

Query return rows whose sum of column value match given sum

I have tables with:
id desc total
1 baskets 25
2 baskets 15
3 baskets 75
4 noodles 10
I would like to ask the query with output which the sum of total is 40.
The output would be like:
id desc total
1 baskets 25
2 baskets 15
I believe this will get you a list of the results you're looking for, but not with your example dataset because nothing in your example dataset can provide a total sum of 40.
SELECT id, desc, total
FROM mytable
WHERE desc IN (
SELECT desc
FROM mytable
GROUP BY desc
HAVING SUM(total) = 40
)
Select Desc,SUM(Total) as SumTotal
from Table
group by desc
having SUM(Total) > = 40
Not quite sure what you want, but this may get you started
SELECT `desc`, SUM(Total) Total
FROM TableName
GROUP BY `desc`
HAVING SUM(Total) = 40
From reading your question, it sounds like you want a query that returns any subset of of sums that represent a certain target value and have the same description.
There is no simple way to do this. This migrates into algorithmic territory.
Assuming I am correct in what you are after, group bys and aggregate functions will not solve your problem. SQL cannot indicate that a query should be performed on subsets of data until it exhaust all possible permutations and finds the Sums that match your requirements.
You will have to intermix an algorithm into your sql ... i.e a stored procedure.
Or simply get all the data from the database that fits the desc then perform your algorithm on it in code.
I recall there was a CS algorithmic class I took where this was a known Problem:
I believe you could just adapt working versions of this algorithm to solve your problem
http://en.wikipedia.org/wiki/Subset_sum_problem
select desc
from (select desc, sum(total) as ct group by desc)