Let's say I have a table, which has four columns (a, b, oper and c) and some primary key column. oper means arithmetic operation (+ - * /) here.
a b oper c
-------------
2 3 + 5
4 2 / 3
6 1 * 9
8 5 - 3
As, we can see in some cases, a <oper> b != c. So, my question is how to filter out such cases?
I've heard of execute, which is used for executing statements, but I don't know how to use it inside where clause.
Also, I'm not generalizing the oper to any arithmetic operation, but it would be nice to know, if any function exists.
SELECT *
FROM table
WHERE с != CASE oper WHEN '+' THEN a+b
WHEN '-' THEN a-b
WHEN '*' THEN a*b
WHEN '/' THEN a/b
ELSE NULL END
fiddle
Related
new to SQL.
I have the following set of data
A X Y Z
1 Wind 1 1
2 Wind 2 1
3 Hail 1 1
4 Flood 1 1
4 Rain 1 1
4 Fire 1 1
I would like to select all distinct 'A' fields where for all rows that contain A have flood and rain.
So in this example, the query would return only the number 4 since for the set of all rows that contain A = 4 we have Flood and Rain.
I need the values of A where for a given value 'a' in A, there exists rows with 'a' that must contain all of the following fields provided (in the example Flood and Rain).
Please let me know if you need further clarification.
I need the values of A where for a given value 'a' in A, there exists rows with 'a' that must contain all of the following fields provided (in the example Flood and Rain).
You can use aggregation, and filter with a having clause:
select a
from mytable t
where x in ('Flood', 'Rain') -- either one or the other
having count(*) = 2 -- both match
If tuples (a, x) tuples are not unique, then you want having count(distinct x) = 2 instead.
You Shooud use count(distinct X) group by A and having
count(distinct...) avoid situation where you have two time the same value for X
select A
from my_table
WHERE x in ('Flood', 'Rain')
group A
having count(distinct X) = 2
What difference it makes if I use, winner IN ('Subject1','Subject2'); & winner='Subject1' OR winner='Subject2';
Queries for the table 17 in the below link:
https://www.w3resource.com/sql-exercises/sql-retrieve-from-table.php#SQLEDITOR
For lists with two elements it doesn't make a difference.
However, MySQL optimizes IN when the list consists of constant expressions. It basically sorts them and does a binary search through the list. This can be a considerable savings with longer lists. As the documentation explains:
If all values are constants, they are evaluated according to the type
of expr and sorted. The search for the item then is done using a
binary search. This means IN is very quick if the IN value list
consists entirely of constants.
In general, IN is safer and does a better job of capturing the column you want. It is very easy to take conditions like this:
where winner = 'Subject1' OR winner = 'Subject2'
and add another condition:
where winner = 'Subject1' or winner = 'Subject2' and
foo = 'bar'
and this logic is probably not longer what you really want -- because it really means:
where winner = 'Subject1' or
(winner = 'Subject2' and foo = 'bar')
This doesn't happen with IN:
where winner in ('Subject1', 'Subject2') and
foo = 'bar'
If there's an index on the column in question, IN vastly out-performs OR. Experience has shown me that the db consistently doesn't use the index when there's an OR on the column.
If there's no index on the column in question, IN out-performs OR if the list is longer than about 5 (it's faster to do a few serial comparisons than traverse a small BTree of values, which is what the DB turns the list into for execution).
IN is also preferred for readability and avoiding SQL's operator precedence trap if brackets are omitted, ie x = a or x = b and c = d is parsed as x = a or (x = b and c = d) instead of the (perhaps) expected (x = a or x = b) and c = d.
Careful when using NOT:
select col1 from
(
select 1 as col1
union all
select 2 as col1
union all
select 3 as col1
union all
select 4 as col1
)x
where x.col1 NOT IN (2,3,4) ;
----------
col1
1
However
select col1 from
(
select 1 as col1
union all
select 2 as col1
union all
select 3 as col1
union all
select 4 as col1
)x
where x.col1 != 2 OR x.col1 != 3 OR x.col1 != 4 ;
---
col1
1
2
3
4
My question is on building indexes when your client is using a lot of little fields.
Consider a search of the following:
(can't change it, this is what the client is providing)
SKU zone1 zone2 zone3 zone4 zone5 zone6 zone7 zone8 zone9 zone10 zone11
A123 1 1 1 1 1 1 1 1
B234 1 1 1 1 1 1 1
C345 1 1 1 1 1 1
But it is much wider, and there are many more categories than just Zone.
The user will be looking for skus that match at least one of the selected zones. I intend to query this with (if the user checked "zone2, zone4, zone6")
select SKU from TABLE1 where (1 IN (zone2,zone4,zone6))
Is there any advantage to indexing with a multi tiered index like so:
create index zones on table1 (zone1,zone2,zone3,zone4,zone5,zone6,zone7,zone8,zone9,zone10,zone11)
Or will that only be beneficial when the user checked zone1?
Thanks,
Rob
You should structure the data as:
create table SKuZones (
Sku int not null,
zone varchar(255)
)
It would be populated with the places where a SKU has a 1. This can then take great advantage of an index on SKUZones(zone) for an index. A query such as:
select SKU
from SKUZones
where zone in ('zone2', 'zone4', 'zone6');
will readily take advantage of an index. However, if the data is not structured in a way appropriate for a relational database, then it is much harder to make queries efficient.
One approach you could take if you can add a column to the table is the following:
Add a new column called zones or something similar.
Use a trigger to populate it with values for each "1" in the columns (so "zone3 zone4 zone5 . . ." for the first row in your data).
Build a full text index on the column.
Run your query using match against
Indexing boolean values is almost always useless.
What if you use a SET datatype? Or BIGINT UNSIGNED?
Let's talk through how to do it with some sized INT, named zones
zone1 is the bottom bit (1<<0 = 1)
zone2 is the next bit (1<<1 = 2)
zone3 is the next bit (1<<2 = 4)
zone4 is the next bit (1<<3 = 8)
etc.
where (1 IN (zone2,zone4,zone6)) becomes
where (zones & 42) != 0.
To check for all 3 zones being set: where (zones & 42) = 42.
As for indexing, no index will help this design; there will still be a table scan.
If there are 11 zones, then SMALLINT UNSIGNED (2 bytes) will suffice. This will be considerably more compact than other designs, hence possibly faster.
For this query, you can have a "covering" index, which helps some:
select SKU from TABLE1 where (zones & 42) != 0 .. INDEX(zones, SKU)
(Edit)
42 = 32 | 8 | 2 = zone6 | zone4 | zone2 -- where | is the bitwise OR operator.
& is the bitwise AND operator. See http://dev.mysql.com/doc/refman/5.6/en/non-typed-operators.html
(zones & 42) = 42 effectively checks that all 3 of those bits are "on".
(zones & 42) = 0 effectively checks that all 3 of those bits are "off".
In both cases, it is ignoring the other bits.
42 could be represented as ((1<<5) | (1<<3) | (1<<1)). Because of precedence rules, I recommend using more parentheses than you might think necessary.
1 << 5 means "shift" 1 by 5 bits.
The problem:
We have a number of entries within a table but we are only interested in the ones that appear in a given sequence. For example we are looking for three specific "GFTitle" entries ('Pearson Grafton','Woolworths (P and O)','QRX - Brisbane'), however they have to appear in a particular order to be considered a valid route. (See image below)
RowNum GFTitle
------------------------------
1 Pearson Grafton
2 Woolworths (P and O)
3 QRX - Brisbane
4 Pearson Grafton
5 Woolworths (P and O)
6 Pearson Grafton
7 QRX - Brisbane
8 Pearson Grafton
9 Pearson Grafton
So rows (1,2,3) satisfy this rule but rows (4,5,6) don't even though the first two entries (4,5) do.
I am sure there is a way to do this via CTE's but some help would be great.
Cheers
This is very simple using even good old tools :-) Try this quick-and-dirty solution, assuming your table name is GFTitles and RowNumber values are sequential:
SELECT a.[RowNum]
,a.[GFTitle]
,b.[GFTitle]
,c.[GFTitle]
FROM [dbo].[GFTitles] as a
join [dbo].[GFTitles] as b on b.RowNumber = a.RowNumber + 1
join [dbo].[GFTitles] as c on c.RowNumber = a.RowNumber + 2
WHERE a.[GFTitle] = 'Pearson Grafton' and
b.[GFTitle] = 'Woolworths (P and O)' and
c.[GFTitle] = 'QRX - Brisbane'
Assuming RowNum has neither duplicates nor gaps, you could try the following method.
Assign row numbers to the sought sequence's items and join the row set to your table on GFTitle.
For every match, calculate the difference between your table's row number and that of the sequence. If there's a matching sequence in your table, the corresponding rows' RowNum differences will be identical.
Count the rows per difference and return only those where the count matches the number of sequence items.
Here's a query that implements the above logic:
WITH SoughtSequence AS (
SELECT *
FROM (
VALUES
(1, 'Pearson Grafton'),
(2, 'Woolworths (P and O)'),
(3, 'QRX - Brisbane')
) x (RowNum, GFTitle)
)
, joined AS (
SELECT
t.*,
SequenceLength = COUNT(*) OVER (PARTITION BY t.RowNum - ss.RowNum)
FROM atable t
INNER JOIN SoughtSequence ss
ON t.GFTitle = ss.GFTitle
)
SELECT
RowNum,
GFTitle
FROM joined
WHERE SequenceLength = (SELECT COUNT(*) FROM SoughtSequence)
;
You can try it at SQL Fiddle too.
I have a table ABR with three columns: two lookup values, A and B and a result Result. I want the result to be returned given A and B
But, for some values of A, column B can be a wildcard. In that case I want the most specific row.
I can't seem to figure out the MySQL statement though - I found one, but it was 7 lines long and had 5 = and two IF's, there must be a better way.
A B Result
1 * First
2 * Second
2 1 Third
2 2 Fourth
3 * Fifth
Example wanted results:
A=1, B=5 -> First
A=1, B=0 -> First
A=2, B=2 -> Fourth
A=2, B=4 -> Second
A=3, B=7 -> Fifth
Do notice that the column values can change later: we can add extra rows for A and B, or remove values. Hardcoding stuff in the SQL is not acceptable.
SELECT Result
FROM ABR
WHERE A = #LookupA
AND (B = #LookupB OR B = '*')
ORDER BY B DESC LIMIT 1;