Comparing two successive rows - mysql

I have Database table payment such as below
level_count | amount
__________________________
650 | 12
1000 | 35
1700 | 50
__________________________
Now Wanted to check if I supplied input as 650 which is level_count column value. Then I should get amount as 12. Then If I supplied input as 999 I should still get 12. Means It should compare its successive rows and compare. Suppose If I enter 1200 then I should get 35 and If I enter 1700 or above I should get 50.
I have tried flowing but didn't got any success.
Where I am going wrong.
SELECT * FROM payment T1
INNER JOIN payment T2 on T1.level_count>=T2.level_count AND T1.level_count<T2.level_count
WHERE T1.level_count = '650'
When I execute above query I get no results.

You may try using a LIMIT query here:
SELECT *
FROM payment
WHERE level_count <= 999 -- or 650, or another input value
ORDER BY level_count DESC
LIMIT 1;
The logic of the above query works in two parts. First, the WHERE clause removes all records for which the level_count is greater than the input value. But this still leaves us potentially with more than one record (which include the record we actually want). The LIMIT trick then keeps the single remaining record with the highest level_count.

Related

mysql: how do I ignore all rows that contain (value column A), if one of these rows has a specific value in column B?

I am looking for a way to filter not only the duplicate rows, but also the "initial" row. The goal is to have a clean list of all positions. The list is used by sales / accounting to see open positions, thats why the initial "Invoice" position has to be removed as well if a "Cancellcation" exists for that invoice.
I've tried solutions with group by, subqueries and EXISTS, but can't get the expected result. Ideally, I get this to work as an additional filter inside the where clause.
Default
ID
Nr
Type
Amount
1
NR-100
Invoice
100
2
NR-101
Invoice
200
3
NR-102
Invoice
300
4
NR-100
Cancellation
100
5
NR-102
Cancellation
300
6
NR-103
Invoice
150
Expected results
ID
Nr
Type
Amount
2
NR-101
Invoice
200
6
NR-103
Invoice
150
EXISTence test would seem to be the way to go so I wonder what problem you had with it..
select *
from t
where type = 'invoice' and
not exists (select 1 from t t1 where t1.nr = t.nr and t1.type = 'cancellation')

why top 5 with ties not giving complete record sets

My query result should give top 5 with ties records but the query mentioned below is giving 130,130,120,120,120,120,120,120,120,120
but I want the result as
130,130,120,120,120,120,120,120,120,120,110,100
select top 5 with ties b.quantity
from dbo.Products as a
inner join dbo.[Order Details] as b
on a.productid = b.productid
inner join dbo.Suppliers as c
on c.supplierid = a.supplierid
order by quantity desc
I am not able to understand why the above query is not including 110 and 100 numbers even after including Top 5 with ties.
According to MSDN about TOP WITH TIES,
WITH TIES may cause more rows to be returned than the value specified
in expression. For example, if expression is set to 5 but 2 additional
rows match the values of the ORDER BY columns in row 5, the result set
will contain 7 rows.
In your example ORDER BY quantity descgives the results as,
130
130
120
120
120
etc
You have specified TOP 5. Which means that the tied values in fifth row will be retrieved by the SELECT query. Here 5th row is 120, so all the 120 will be retrieved.
SQL Fiddle
Ordered list is cut at the 5th row. But all rows with the same "rank" come in too, otherwise it would be rather random, which of them is included and which is not.

ORDER BY and GROUP BY those results in a single query

I am trying to query a dataset from a single table, which contains quiz answers/entries from multiple users. I want to pull out the highest scoring entry from each individual user.
My data looks like the following:
ID TP_ID quiz_id name num_questions correct incorrect percent created_at
1 10154312970149546 1 Joe 3 2 1 67 2015-09-20 22:47:10
2 10154312970149546 1 Joe 3 3 0 100 2015-09-21 20:15:20
3 125564674465289 1 Test User 3 1 2 33 2015-09-23 08:07:18
4 10153627558393996 1 Bob 3 3 0 100 2015-09-23 11:27:02
My query looks like the following:
SELECT * FROM `entries`
WHERE `TP_ID` IN('10153627558393996', '10154312970149546')
GROUP BY `TP_ID`
ORDER BY `correct` DESC
In my mind, what that should do is get the two users from the IN clause, order them by the number of correct answers and then group them together, so I should be left with the 2 highest scores from those two users.
In reality it's giving me two results, but the one from Joe gives me the lower of the two values (2), with Bob first with a score of 3. Swapping to ASC ordering keeps the scores the same but places Joe first.
So, how could I achieve what I need?
You're after the groupwise maximum, which can be obtained by joining the grouped results back to the table:
SELECT * FROM entries NATURAL JOIN (
SELECT TP_ID, MAX(correct) correct
FROM entries
WHERE TP_ID IN ('10153627558393996', '10154312970149546')
GROUP BY TP_ID
) t
Of course, if a user has multiple records with the maximal score, it will return all of them; should you only want some subset, you'll need to express the logic for determining which.
MySql is quite lax when it comes to group-by-clauses - but as a rule of thumb you should try to follow the rule that other DBMSs enforce:
In a group-by-query each column should either be part of the group-by-clause or contain a column-function.
For your query I would suggest:
SELECT `TP_ID`,`name`,max(`correct`) FROM `entries`
WHERE `TP_ID` IN('10153627558393996', '10154312970149546')
GROUP BY `TP_ID`,`name`
Since your table seems quite denormalized the group by name-par could be omitted, but it might be necessary in other cases.
ORDER BY is only used to specify in which order the results are returned but does nothing about what results are returned - so you need to apply the max()-function to get the highest number of right answers.

MySQL Query Nearest Lowest Value & Actual Value Issue

I have table
'test' with fileds & values
id number
1 13
2 17
3 20
4 30
5 40
If i provide 14,15,16 then it should give me 13.
If i provide 22,24,24 then it should give me 20.
If i provide 13 then it should give me 13.
If i provide 20 then it should give me 20.
I'm looking for mysql query for this which provide nearest lowest value & actual value.
Assuming you send a table with a list of values
SELECT
*
FROM
MyTable
WHERE
number <= (SELECT MIN(requireNuber) FROM InputTable)
ORDER BY
number DESC
LIMIT 1
Although, I'm sure you could only send the lowest value to MySQL from the client (why can't you) which would make the code look like
SELECT
*
FROM
MyTable
WHERE
number <= #MyParameter
ORDER BY
number DESC
LIMIT 1

Obtain running frequency distribution from previous N rows of MySQL database

I have a MySQL database where one column contains status codes. The column is of type int and the values will only ever be 100,200,300,400. It looks like below; other columns removed for clarity.
id | status
----------------
1 300
2 100
3 100
4 200
5 300
6 300
7 100
8 400
9 200
10 300
11 100
12 400
13 400
14 400
15 300
16 300
The id field is auto-generated and will always be sequential. I want to have a third column displaying a comma-separated string of the frequency distribution of the status codes of the previous 10 rows. It should look like this.
id | status | freq
-----------------------------------
1 300
2 100
3 100
4 200
5 200
6 300
7 100
8 400
9 300
10 300
11 100 300,100,200,400 -- from rows 1-10
12 400 100,300,200,400 -- from rows 2-11
13 400 100,300,200,400 -- from rows 3-12
14 400 300,400,100,200 -- from rows 4-13
15 300 400,300,100,200 -- from rows 5-14
16 300 300,400,100 -- from rows 6-15
I want the most frequent code listed first. And where two status codes have the same frequency it doesn't matter to me which is listed first but I did list the smaller code before the larger in the example. Lastly, where a code doesn't appear at all in the previous ten rows, it shouldn't be listed in the freq column either.
And to be very clear the row number that the frequency string appears on does NOT take into account the status code of that row; it's only the previous rows.
So what have I done? I'm pretty green with SQL. I'm a programmer and I find this SQL language a tad odd to get used to. I managed the following self-join select statement.
select *, avg(b.status) freq
from sample a
join sample b
on (b.id < a.id) and (b.id > a.id - 11)
where a.id > 10
group by a.id;
Using the aggregate function avg, I can at least demonstrate the concept. The derived table b provides the correct rows to the avg function but I just can't figure out the multi-step process of counting and grouping rows from b to get a frequency distribution and then collapse the frequency rows into a single string value.
Also I've tried using standard stored functions and procedures in place of the built-in aggregate functions, but it seems the b derived table is out of scope or something. I can't seem to access it. And from what I understand writing a custom aggregate function is not possible for me as it seems to require developing in C, something I'm not trained for.
Here's sql to load up the sample.
create table sample (
id int NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
status int
);
insert into sample(status) values(300),(100),(100),(200),(200),(300)
,(100),(400),(300),(300),(100),(400),(400),(400),(300),(300),(300)
,(100),(400),(100),(100),(200),(500),(300),(100),(400),(200),(100)
,(500),(300);
The sample has 30 rows of data to work with. I know it's a long question, but I just wanted to be as detailed as I could be. I've worked on this for a few days now and would really like to get it done.
Thanks for your help.
The only way I know of to do what you're asking is to use a BEFORE INSERT trigger. It has to be BEFORE INSERT because you want to update a value in the row being inserted, which can only be done in a BEFORE trigger. Unfortunately, that also means it won't have been assigned an ID yet, so hopefully it's safe to assume that at the time a new record is inserted, the last 10 records in the table are the ones you're interested in. Your trigger will need to get the values of the last 10 ID's and use the GROUP_CONCAT function to join them into a single string, ordered by the COUNT. I've been using SQL Server mostly and I don't have access to a MySQL server at the moment to test this, but hopefully my syntax will be close enough to at least get you moving in the right direction:
create trigger sample_trigger BEFORE INSERT ON sample
FOR EACH ROW
BEGIN
DECLARE _freq varchar(50);
SELECT GROUP_CONCAT(tbl.status ORDER BY tbl.Occurrences) INTO _freq
FROM (SELECT status, COUNT(*) AS Occurrences, 1 AS grp FROM sample ORDER BY id DESC LIMIT 10) AS tbl
GROUP BY tbl.grp
SET new.freq = _freq;
END
SELECT id, GROUP_CONCAT(status ORDER BY freq desc) FROM
(SELECT a.id as id, b.status, COUNT(*) as freq
FROM
sample a
JOIN
sample b ON (b.id < a.id) AND (b.id > a.id - 11)
WHERE
a.id > 10
GROUP BY a.id, b.status) AS sub
GROUP BY id;
SQL Fiddle