I have been little bit confused while reading MySQL documentation MySQL documentation
A row subquery is a subquery variant that returns a single row and can thus return more than one column value. Legal operators for row subquery comparisons are:
= > < >= <= <> != <=>
For "=" documentation provides good explanation:
SELECT * FROM t1 WHERE (column1,column2) = (1,1);
is same as:
SELECT * FROM t1 WHERE column1 = 1 AND column2 = 1;
But how do the ">" or "<" can compare two rows? What is "plain" variant for this operator?
create table example(a integer,b integer);
insert into example values (1,1);
insert into example values (1,2);
insert into example values (2,1);
select * from example where (a,b) > (1,1)
a | b
-----
1 | 2
2 | 1
Playground: http://www.sqlfiddle.com/#!9/88641/2
p.s.:
PostgreSQL have same behaviour.
Oracle fail with error "ORA-01796: this operator cannot be used with lists "
MySQL and PostgreSQL has same behaviour, let's read postgres documentation
The = and <> cases work slightly differently from the others. Two rows are considered equal if all their corresponding members are non-null and equal; the rows are unequal if any corresponding members are non-null and unequal; otherwise the result of the row comparison is unknown (null).
For the <, <=, > and >= cases, the row elements are compared left-to-right, stopping as soon as an unequal or null pair of elements is found. If either of this pair of elements is null, the result of the row comparison is unknown (null); otherwise comparison of this pair of elements determines the result. For example, ROW(1,2,NULL) < ROW(1,3,0) yields true, not null, because the third pair of elements are not considered.
So (a,b) = (c,d) evaluated as
a = c and b = d
But (a,b,) < (c,d) evaluated as
a < b or (a=c and b < c)
It's look very strange. And I have no guess how will be evaluated comparation of rows with 3 of more attributes;
Related
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
Suppose I have a table A with a primary key
PRIMARY KEY(c1,c2)
and the cardinality of c1 is very low, whereas c2 is very high
When executing the following queries,
select *
from A
where (c1, c2) in (('001', 'aaa'))
select *
from A
where c1 = '001'and c2 = 'aaa'
the optimizer uses the index.
However, for the following cases,
Case 1:
select *
from A
where (c1, c2) in (('001', 'aaa'), ('002', 'bbb'), ('003', 'ccc'))
Case 2:
select *
from A
where (c1 = '001'and c2 = 'aaa') or
(c1 = '002'and c2 = 'bbb') or
(c1 = '003'and c2 = 'ccc')
the optimizer stops using the index for the Case 1 but still uses for the Case 2.
What makes the optimizer stop using the index for Case 1?
*MySQL Version: 5.6.10
Although those expressions are semantically equivalent, MySQL only added the Range Optimization of Row Constructor Expressions, which is actually able to execute them the same way, in MySQL 5.7.3 (emphasis mine):
The optimizer now is able to apply the range scan access method to queries of this form:
SELECT ... FROM t1 WHERE ( col_1, col_2 ) IN (( 'a', 'b' ), ( 'c', 'd' ));
Previously, for range scans to be used it was necessary for the query to be written as:
SELECT ... FROM t1 WHERE ( col_1 = 'a' AND col_2 = 'b' )
OR ( col_1 = 'c' AND col_2 = 'd' );
For the optimizer to use a range scan, queries must satisfy these conditions:
Only IN() predicates are used, not NOT IN().
On the left side of the IN() predicate, the row constructor contains only column references.
On the right side of the IN() predicate, row constructors contain only runtime constants, which are either literals or local column references that are bound to constants during execution.
On the right side of the IN() predicate, there is more than one row constructor.
How can i get only Lines, where 2 (or more) Where Clause of many are true?
For Example:
SELECT *
FROM A
WHERE CONVERT(AES_DECRYPT(name,'".$this->key."'),CHAR) LIKE '%".$this->escape($name)."%'"
OR CONVERT(AES_DECRYPT(name2,'".$this->key."'),CHAR) = '".$this->escape($name2)."'"
OR CONVERT(AES_DECRYPT(name3,'".$this->key."'),CHAR) = '".$this->escape($name3)."'"
OR CONVERT(AES_DECRYPT(name4,'".$this->key."'),CHAR) = '".$this->escape($name4)."'"
.....
is there a simply way to get only the results where 2 (or more) match, without creating a huge SQL Statement (each column with each other column - (name AND name2) OR (name AND name3) OR (name AND name4) OR ... )
In MySQL you can utilise the fact that it treats boolean expressions as 1 or 0 in a numeric context. Thus to check for 2 or more conditions being true, you can write
WHERE (condition 1) + (condition 2) + ... + (condition n) >= 2
Note that the parentheses around each condition are required to prevent any operator precedence issues.
SELECT * FROM table WHERE (a!=1 AND b!=2)
How can I select all rows where a is not 1 and b is not 2 in the same time?
That is - in PHP it'd be ($a!=1 && $b!=2), bringing rows for example like
a=1, b=6,
a=5, b=2,
...
(but not a=1, b=2)
What I am getting with MySQL equals to PHP's $a!=1 && $b!=2 (the same just without brackets), bringing rows like
a=7, b=9,
a=4, b=0,
...
(but never a=1, and never b=2)
Based on your example, you want or, not and:
SELECT t.*
FROM table t
WHERE a <> 1 OR b <> 2;
Although MySQL (and other databases) support != for inequality, the standard is =. If you want to allow NULL values, you might want:
SELECT t.*
FROM table t
WHERE a <=> 1 OR b <=> 2;
<=> is a NULL-safe inequality operator specific to MySQL.
I am trying to select rows from a table where one of the (NVARCHAR) columns is within a numeric range.
SELECT ID, Value
FROM Data
WHERE ISNUMERIC(Value) = 1 AND CONVERT(FLOAT, Value) < 66.6
Unfortunately as part of the SQL spec the AND clauses don't have to short circuit (and don't on MSSQL Server EE 2008). More info: Is the SQL WHERE clause short-circuit evaluated?
My next attempt was to try this to see if I could achieve delayed evaluation of the CONVERT
SELECT ID, Value
FROM Data
WHERE (CASE WHEN ISNUMERIC(Value) = 1 THEN CONVERT(FLOAT, Value) < 66.6 ELSE 0 END)
but I cannot seem to use a < (or any comparison) with the result of a CONVERT. It fails with the error
Incorrect syntax near '<'.
I can get away with
SELECT ID, CONVERT(FLOAT, Value) AS Value
FROM Data
WHERE ISNUMERIC(Value) = 1
So the obvious solution is to wrap the whole select statement in another SELECT and WHERE and return the converted values from the inner select and filter in there where of the outer select. Unfortunately this is where my Linq-to-sql problem comes in. I am filtering not only by one range but potentialy by many, or just by the existance of the record (there are some date range selects and comparisons I've left out.)
Essentially I would like to be able to generate something like this:
SELECT ID, TypeID, Value
FROM Data
WHERE (TypeID = 4 AND ISNUMERIC(Value) AND CONVERT(Float, Value) < 66.6)
OR (TypeID = 8 AND ISNUMERIC(Value) AND CONVERT(Float, Value) > 99)
OR (TypeID = 9)
(With some other clauses in each of those where options.) This clearly doesn't work if I filter out the non-ISNUMERIC values in an inner select.
As I mentioned I am using Linq-to-sql (and PredicateBulider) to build up these queries but unfortunately
Datas.Where(x => ISNUMERIC(x.Value) ? Convert.ToDouble(x.Value) < 66.6 : false)
Gets converted to this which fails the initial problem.
WHERE (ISNUMERIC([t0].[Value]) = 1) AND ((CONVERT(Float,[t0].[Value])) < #p0)
My last resort will have to be to outer join against a double select on the same table for each of the comparisons but this isn't really an idea solution. I was wondering if anyone has run into similar issues before?
I dont think the issue is the AND itself, but rather the convertion from NVARCHAR to FLOAT
Have a look at the following example
DECLARE #Table TABLE(
Value NVARCHAR(10)
)
INSERT INTO #Table SELECT '1'
INSERT INTO #Table SELECT '100'
INSERT INTO #Table SELECT 'A'
SELECT *
FROM #Table
WHERE ISNUMERIC(Value)= 1 AND CAST(CAST(Value AS VARCHAR(10)) AS FLOAT) > 50
SELECT *
FROM #Table
WHERE ISNUMERIC(Value)= 1 AND CAST(Value AS FLOAT) > 50
The last select is where I get the error stating
Msg 8114, Level 16, State 5, Line 13
Error converting data type nvarchar to
float.
But the first select works fine.