selecting only values having difference greater than 0 (no negatives and 0) - sql

i've two columns in mysql db table votes : accept and reject
i want to query only the top result after the accept-reject column values
table : votes
=================
accept | reject
=================
7 | 9
5 | 1
2 | 15
5 | 1
i want just the positive and top value, here 5-1 = 4
actually, i've lot of other columns along with accept and reject..i just want to ORDER the results by the top value of difference(positive only) and LIMIT it to 1 so that i can get the one row i want..was i clear? :)
how to write query for this?
thanks..

Use:
SELECT t.accept,
t.reject,
t.accept - t.reject AS difference
FROM VOTES t
WHERE t.accept - t.reject > 0
ORDER BY difference DESC
LIMIT 1
Alternate using subquery:
SELECT v.accept,
v.reject
FROM VOTES v
WHERE v.accept - v.reject > 0
JOIN (SELECT MAX(t.accept - t.reject) AS difference
FROM VOTES t
WHERE t.accept - t.reject > 0) x ON x.difference = (v.accept - v.reject)

SELECT (accept - reject) as top_value FROM table WHERE (accept - reject) > 0
So if you have values like this:
=============================
accept | reject | top_value
=============================
5 | 1 | 4
7 | 9 | -2
2 | 15 | -13
5 | 5 | 0
the query will select only row 1.

I'm assuming those two columns are just an extract from your table, and you want the entire row where (accept-reject) is maximum. Then you can do it like this:
SELECT * FROM votes
WHERE accept-reject = (SELECT MAX(accept-reject) FROM votes)
LIMIT 1

Related

How to use SQL to filter off rows that are in excess (but have not just met) threshold limit?

I have a table like the following:
ID|PROMOTION|USER_ID|LIMIT|CUMULATIVE_USAGE|TDATE
01|111111111|AAAAAAA| 2 | 1 |07-21-2020
02|111111111|AAAAAAA| 2 | 3 |07-22-2020
03|111111111|AAAAAAA| 2 | 5 |07-23-2020 <-- remove
04|222222222|AAAAAAA| 4 | 1 |08-21-2020
05|222222222|AAAAAAA| 4 | 3 |08-22-2020
06|222222222|AAAAAAA| 4 | 5 |08-23-2020
07|333333333|AAAAAAA| 5 | 1 |09-21-2020
08|333333333|AAAAAAA| 5 | 3 |09-22-2020
09|333333333|AAAAAAA| 5 | 5 |09-23-2020
For each user/promotion id, I want to filter off rows that did not just cross the limit but had already been in excess of the limit i.e. row where ID=3 in this case.
What SQL logic could I use to do this?
You can use lag():
select t.*
from (select t.*,
lag(cumulative_usage) over (partition by promotion, user_id order by id) as prev_cumulative_usage
from t
) t
where usage <= prev_cumulative_usage or
prev_cumulative_usage is null;

repeating / duplicating query entries based on a table value

Related to / copied from this PostgreSQL topic: so-link
Let's say I have a table with two rows
id | value |
----+-------+
1 | 2 |
2 | 3 |
I want to write a query that will duplicate (repeat) each row based on
the value. I want this result (5 rows total):
id | value |
----+-------+
1 | 2 |
1 | 2 |
2 | 3 |
2 | 3 |
2 | 3 |
How is this possible in SQL Anywhere (Sybase SQL)?
The easiest way to do this is to have a numbers table . . . one that generates integers. Perhaps you have one handy. There are other ways. For instance, using a recursive CTE:
with numbers as (
select 1 as n
union all
select n + 1
from numbers
where n < 100
)
select t.*
from yourtable t join
numbers n
on n.n <= value;
Not all versions of Sybase necessarily support recursive CTEs There are other ways to generate such a table or you might already have one handy.

Filtering data with from statement

So let's say I have this table with these rows in it
Table name: MYTABLE
ID | NUMBER | FK_ID
1 | 0 | 26
2 | 0 | 26
3 | 1 | 26
4 | 0 | 27
5 | 1 | 27
Now I want to filter out only the rows that that go under the same FK_ID and have two or more NUMBER 0's in them.
So for instance if I would apply this filter here, I would only see one row which corresponds to the FK_ID 26 because it has two NUMBER 0s in it's MYTABLE data.
Is this even possible to do or should I just handle the whole data in my programming language not filter it like that from DB.
SELECT FK_ID ,
COUNT(DECODE(NUMBER ,0,1))
FROM TEST_DATA
GROUP BY FK_ID
HAVING COUNT(DECODE(NUMBER ,0,1)) >= 2
Fiddle here : http://sqlfiddle.com/#!4/44d70/4
Does this query work for you?
SELECT
FK_ID
FROM MYTABLE
WHERE NUMBER = 0
GROUP BY FK_ID
HAVING COUNT(*) >= 2;
Also, consider renaming the NUMBER column, as NUMBER is a reserved word in Oracle.

select max value in a group of consecutive values

How do you do to retrieve only the max value of a group with only consecutive values?
I have a telephone database with only unique values and I want to get only the highest number of each telephone number group TelNr and I am struggling.
id | TeNr | Position
1 | 100 | SLMO2.1.3
2 | 101 | SLMO2.3.4
3 | 103 | SLMO2.4.1
4 | 104 | SLMO2.3.2
5 | 200 | SLMO2.5.1
6 | 201 | SLMO2.5.2
7 | 204 | SLMO2.5.5
8 | 300 | SLMO2.3.5
9 | 301 | SLMO2.6.2
10 | 401 | SLMO2.4.8
Result should be:
TelNr
101
104
201
204
301
401
I have tried almost every tip I could find so far and whether I get all TelNr or no number at all which is useless in my case.
Any brilliant idea to run this with SQLITE?
So you're searching for gaps and want to get the first value of those gaps.
This is probably the best way to get them, try to check for a row with the current TeNr plus 1 and if there's none you found it:
select t1.TeNr, t1.TeNr + 1 as unused_TeNr
from tab as t1
left join Tab as t2
on t2.TeNr = t1.TeNr + 1
where t2.TeNr is null
Edit:
To get the range of missing values you need to use some old-style SQL as SQLite doesn't seem to support ROW_NUMBER, etc.
select
TeNr + 1 as RangeStart,
nextTeNr - 1 as RangeEnd,
nextTeNr - TeNr - 1 as cnt
from
(
select TeNr,
( select min(TeNr) from tab as t2
where t2.TeNr > t1.TeNr ) as nextTeNr
from tab as t1
) as dt
where nextTeNr > TeNr + 1
It's probably not very efficient, but might be ok if the number of rows is small and/or there's a index on TeNr.
Getting each value in the gap as a row in your result set is very hard, if your version of SQLite supports recursive queries:
with recursive cte (TeNr, missing, maxTeNr) as
(
select
min(TeNr) as TeNr, -- start of range of existing numbers
0 as missing, -- 0 = TeNr exists, 1 = TeNr is missing
max(TeNr) as maxTeNr -- end of range of existing numbers
from tab
union all
select
cte.TeNr + 1, -- next TeNr, if it doesn't exists tab.TeNr will be NULL
case when tab.TeNr is not null then 0 else 1 end,
maxTeNr
from cte left join tab
on tab.TeNr = cte.TeNr + 1
where cte.TeNr + 1 < maxTeNr
)
select TeNr
from cte
where missing = 1
Depending on your data this might return a huge amount of rows.
You might also use the result of the previous RangeStart/RangeEnd query as input to this recursion.

Matching algorithm in SQL

I have the following table in my database.
# select * FROM matches;
name | prop | rank
------+------+-------
carl | 1 | 4
carl | 2 | 3
carl | 3 | 9
alex | 1 | 8
alex | 2 | 5
alex | 3 | 6
alex | 3 | 8
alex | 2 | 11
anna | 3 | 8
anna | 3 | 13
anna | 2 | 14
(11 rows)
Each person is ranked at work by different properties/criterias called 'prop' and the performance is called 'rank'. The table contains multiple values of (name, prop) as the example shows. I want to get the best candidate following from some requirements. E.g. I need a candidate that have (prop=1 AND rank > 5) and (prop=3 AND rank >= 8). Then we must be able to sort the candidates by their rankings to get the best candidate.
EDIT: Each person must fulfill ALL requirements
How can I do this in SQL?
select x.name, max(x.rank)
from matches x
join (
select name from matches where prop = 1 AND rank > 5
intersect
select name from matches where prop = 3 AND rank >= 8
) y
on x.name = y.name
group by x.name
order by max(rank);
Filtering the data to match your criteria here is quite simple (as shown by both Amir and sternze):
SELECT *
FROM matches
WHERE prop=1 AND rank>5) OR (prop=3 AND rank>=8
The problem is how to aggregate this data so as to have just one row per candidate.
I suggest you do something like this:
SELECT m.name,
MAX(DeltaRank1) AS MaxDeltaRank1,
MAX(DeltaRank3) AS MaxDeltaRank3
FROM (
SELECT name,
(CASE WHEN prop=1 THEN rank-6 ELSE 0 END) AS DeltaRank1,
(CASE WHEN prop=3 THEN rank-8 ELSE 0 END) AS DeltaRank3,
FROM matches
) m
GROUP BY m.name
HAVING MaxDeltaRank1>0 AND MaxDeltaRank3>0
SORT BY MaxDeltaRank1+MaxDeltaRank3 DESC;
This will order the candidates by the sum of how much they exceeded the target rank in prop1 and prop3. You could use different logic to indicate which is best though.
In the case above, this should be the result:
name | MaxDeltaRank1 | MaxDeltaRank3
------+---------------+--------------
alex | 3 | 0
... because neither anna nor carl reach both the required ranks.
A typical case of relational division. We assembled a whole arsenal of techniques under this related question:
How to filter SQL results in a has-many-through relation
Assuming you want the minimum rank of a person, I might solve your particular case with LEAST():
SELECT m1.name, LEAST(m1.rank, m2.rank, ...) AS best_rank
FROM matches m1
JOIN matches m2 USING (name)
...
WHERE m1.prop = 1 AND m1.rank > 5
AND m2.prop = 3 AND m2.rank >= 8
...
ORDER BY best_rank;
Also assuming name to be unique per individual person. You'd probably use some kind of foreign key to a pk column of a person table in reality.
And if you have such a person table like you should, the best rank would be stored in a column there ...
If I understand you question, then you just need to execute the following operation:
SELECT * FROM matches where (prop = 1 AND rank > 5) OR (prop = 3 AND rank >= 8) ORDER BY rank
It gives you the canidates that either have prop=1 and rank > 5 or prop=3 and rank >= 8 sorted by their rankings.