Query with offset returns overlapping data sets - mysql

Initial attempts at getting a very simple pagination, using fetch n rows and then a subsequent call with offset, gives overlapping entries in Oracle.
I was expecting the following to give me two unique sets of results. 1-100 and then 101-200 of the results that would have been returned if the first line had been set with a limit of 200.
select * from "APPR" /*+ index(APPR APPR_IDX01) */ where ("APPROVER" = 'A') or ("APPROVER" > 'A') order by "APPROVER" fetch first 100 rows only ;
select * from "APPR" /*+ index(APPR APPR_IDX01) */ where ("APPROVER" = 'A') or ("APPROVER" > 'A') order by "APPROVER" offset 100 rows fetch next 100 rows only ;
So if there are 150 items for approver A the first results should be:
A, item1
....
A, item100
The subsequent call (offset by 100) giving
A, item101
...
A, item150
B, item1
B, item2
....
B, item201
Unfortunately the second set contains some entries from the first batch of values. Probably a really silly error, but I can't find an explanation as to why this should happen.
---- Updated as a result of comments
The Primary key consists of Approver and several other fields which together form a composite and unique primary.
The code will be called through ODBC and will be used on Oracle and MySQL back-end.

In Oracle, if you make "order by" to a column containing same values (like you have - 'A', 'A', 'A' ...) the order of records inside 'A' values will be random.
Please try to change your queries to ... order by "APPROVER", rowid ...

Presumably, APPROVER is not a unique column. Since there may be duplicates, the order by claus is not stable, and the offset clause might generate duplicates.
A simple solution is to add more columns to the order by to break the ties. Assuming that (approver, item) is a unique set of columns, that would be:
select *
from appr
where approver = 'A' or approver > 'A'
order by approver, item
fetch first 100 rows only
-- then: offset 100 rows fetch next 100 rows only
Notes:
there is no need to surround all-caps identifiers (tables or column names) with double quotes: that's the default in Oracle already
parentheses around the or conditions are superfluous in this simple case
if approver is always one character long, then the where clause can be simplified as where approver >= 'A'
use index hints only if you really know why you are doing it (I am not saying you don't, but I removed it, just in case); most of the time, the database knows better

Related

How do I find rows in a database that are not in the expected order?

Say I have a table with the structure
recordNumber: INTEGER (autoincrement)
insertedOn: DATETIME
Normally data gets inserted into the table it increments the recordNumber and insertedOn is always current time. Normally the following should be true
select insertedOn order by recordNumber === select insertedOn order by insertedOn
But that's actually not the case the question I have is how do I query the database so I can find the first recordNumber that would break the condition.
You can use LAG window function, but it depends on the particular database you are using.
If we assume that your increments are by '1', then this is a little more generic:
select top 1 *
from YrTbl Ths
where exists (select 1
from YrTbl Prev
where Prev.recordNumber+1=Ths.recordNumber
and Prev.insertedOn>=Ths.insertedOn
)
order by Ths.RecordNumber
TOP n might work a little differently in your environment; if you are using MySQL you might like to use LIMIT 1 at the end of the query, for example.

SQL SELECT everthing with value 5 but not specify from which column

I have tried to select something with SQL, and I've a problem with it.
What I want:
SQL SELECT * FROM table WHERE ? = '5';
Select everything which = 5, BUT not specify from which column.
Example:
From this ""database"", you should receive the 1st and the last row.
Is that possible?
You have to list the columns but you can use in. The where clause looks like:
where 5 in (price, height)
Note: This assumes that the columns have the same type. You could get type conversion errors if they are not.
Also, given the names of the column and the data, I assume that the columns are stored as numbers. Hence, I dropped the single quotes around 5. If they are really strings, then use the single quotes.
you need to add a condition to your query with or keyword so if any of them match the row will be shown as a result
SELECT * FROM tablename WHERE price =5 or height= 5
better you list your columns by name instead of using * after SELECT

MySQL query - condition (comparing strings) does not work

My table has only got 1 Column 'Name' with 100 unique entries.
I need to find out if a given Value exists in that table.
I am using:
SELECT 1 FROM `tbl_names` WHERE `Name` = "Lisa"
MySQL returns an empty result, so no 0 for not found and no 1 for found, even though the given name is an entry in that table.
What am I missing here?
select count(*) from tbl_name where name = 'Lisa' - will return count of entries with Lisa in the column. You can do as before with select 1, and calculate results - zero size means no occurance
If you want to return as 0 or 1, I would suggest:
select (exists (select 1 from tbl_names where Name = 'Lisa')) as flag
This will not fix the problem that you describe -- but it will always return one row with a single column whose value is 0 or 1.
'Lisa' is not in the table. You might try where Name like '%Lisa%'.

Missing values in a query

I encounter some strange results in the following query :
SET #indi_id = 768;
SET #generations = 8;
SELECT num, sosa, seq, len, dernier, ful_ful_nom
FROM fullindi
LEFT JOIN lignee_new
ON ((ful_indi_id = dernier) AND (len BETWEEN 1 AND #generations))
RIGHT JOIN numbers
ON ((sosa = num) AND (premier = #indi_id))
WHERE num BETWEEN 1 AND pow(2, #generations)
GROUP BY num
ORDER BY num;
The result looks like this :
Why the row just before a full NULL one doesn't display the existing values 'sosa', 'len', 'dernier', ful_ful_nom') but only the 'seq' value (see rows 43 and 47 in this example) ?
What am I missing?
As requested, here are data :
table lignee_new :
table fullindi :
The problem is that MySQL does really dumb things when an Aggregate function is introduced, or a GROUP BY is included, but not all of the fields are in an Aggregate Function or your GROUP BY.
You are asking it to GROUP BY num but none of the other columns in your SELECT are included in the Group BY nor are they being aggregated with a function (SUM, MAX, MIN, AVG, etc..)
In any other RDBMS this query wouldn't run and would throw an error, but MySQL just carries on. It uses the logic to decide which value it should show for each field that isn't num by just grabbing the first value it finds in it's data storage which may be different between innoDB and whatever else folks use anymore.
My guess is that in your case you have more than one record in lignee_new that has a num of 43. Since you GROUP BY num and nothing else, it just grabs values randomly from your multiple records where num=43 and displays them... which is reasonable. By not including them in an aggregate function you are pretty much saying "I don't care what you display for these other fields, just bring something back" and so MySQL does.
Remove your GROUP BY clause completely and you'll see data that makes sense. Perhaps use WHERE to further filter your records to get rid of nulls or other things you don't need (don't use GROUP BY to filter).

Mysql select, if input arguments are same dont return any rows

I have a bit of an odd mysql query I need to run.
I am passing two arguments to a WHERE clause, $source and $destination (via php). I want mysql to simply return nothing if $source and destination are the same number. Otherwise do the lookup in the DB and pull the record.
Query:
SELECT count(id) AS count
FROM approval
WHERE source=1 AND destination=2 AND approved!=0
Now this will return all rows where source is one and destination is two.
BUT! I want it it to return nothing if source and destination are the same number.
Some rows in certain cases may contain the same number, but that happens in a different potion of the code, in this specific select I want it only to perform the search if source and destination are different. I looked at comparison operators but those all seem to be checking the argument against a value in the column, rather than an argument against an argument.
I think this query does what you want
SELECT count(id) AS count
FROM approvals
WHERE source = $source AND destination = $destination AND approved <> 0
HAVING $source <> $destination;
That would be a simple matter of including:
and source <> destination
in the query. This will exclude rows where the columns are the same which, because of the other clauses, means the arguments you've provided are the same as well.
This will probably still go out to the database but it's guaranteed to return a count of zero (assuming no NULLs of course - if you want to handle those, you'll need extra clauses with IS [NOT] NULL). If you don't even want to go out to the database if your two arguments are the same, the time to do that is in your PHP code by comparing the arguments beforehand.
By way of example, the following DDL:
create table xyzzy (a int, b int);
insert into xyzzy (a,b) values (1,1);
creates a table with one row. When you execute the queries:
SELECT count(a) AS x FROM xyzzy WHERE a = 1 AND b = 1;
SELECT count(a) AS x FROM xyzzy WHERE a = 1 AND b = 1 and a <> b;
you'll get the rows 1 (equivalent to what you currently have) and 0 (equivalent to the change I've proposed).
If you really want to use the parameters instead of the columns, that will work as well:
SELECT count(a) AS x FROM xyzzy WHERE a = 1 AND b = 1 and 1 <> 1;
This also returns a count of zero and is equivalent to just tacking:
and $source <> $destination
onto your query. If the two numbers are identical, this will boil down to and false which will result in a count of zero.
So will either of:
and source <> $destination
and $source <> destination
for that matter.