10 performance tips we tend to forget

Post on 27-Jan-2017

1.316 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

10 PERFORMANCE TIPS WE TEND TO

FORGETEli Aschkenasy@EliAschkenasy

#JDNL16April 16, 2016

Eli Aschkenasy - @EliAschkenasy - #JDNL16

10 performance tips we tend to forget

3 Categories

1. Indexing2. Query Optimization3. Resource Handling

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Indexes have two purposes:

1. Speed up access to data2. (help) Enforce constraints

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Query Optimization

Happens in conjunction with index strategy and only with real data.

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling

Might be the most overlooked yet easiest-to-fix part, but requires real data as well.

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Gather Data

Eli Aschkenasy - @EliAschkenasy - #JDNL16

DB Optimization – Logging Queries

mysql> SET GLOBAL slow_query_log = ON;

mysql> SET GLOBAL long_query_time = 0;

mysql> SET GLOBAL log_queries_not_using_indexes = ON;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

DB Optimization – Logging Queries

Change to ON

Eli Aschkenasy - @EliAschkenasy - #JDNL16

DB Optimization – Analyzing Queries

Percona Toolkit for MySQL

Eli Aschkenasy - @EliAschkenasy - #JDNL16

DB Optimization – Analyzing Queries

http://nk.gl/slow_queries/analyze

For those of us who don’t want to commit.

Eli Aschkenasy - @EliAschkenasy - #JDNL16

WITH THIS DATAWE CAN CHECK THE IMPLEMENTATION

Eli Aschkenasy - @EliAschkenasy - #JDNL16

DB Optimization

Eli Aschkenasy - @EliAschkenasy - #JDNL16

DB Optimization

Here is my schema, what indexes do I need?

Eli Aschkenasy - @EliAschkenasy - #JDNL16

DB Optimization

Index choice depends on the queries you run, not the data you have!

relational schema design is based on

DATA

indexdesign is based on

QUERIES

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’

works

Simple/Compound Searches

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’

works

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’AND f_name = ‘Hans’

works

Simple/Compound Searches

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’

works

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’AND f_name = ‘Hans’

works

SELECT * FROM PhoneBookWHERE f_name = ‘Hans’

doesn‘t work

Simple/Compound Searches

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name LIKE ‘Ku%’

works

Range Lookup

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name LIKE ‘Ku%’

works

SELECT * FROM PhoneBookWHERE l_name = ‘Ku%’AND f_name = ‘Hans’

doesn‘t work

Range Lookup

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name LIKE ‘Ku%’

works

SELECT * FROM PhoneBookWHERE l_name = ‘Ku%’AND f_name = ‘Hans’

doesn‘t work

Range Lookup

Tip #1:Remember: Any Range Comparison breaks index benefit for subsequent columns

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’ORDER BY f_name

works

Sorting

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’ORDER BY f_name

works

Sorting

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’ORDER BY phone

doesn‘t work

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’ORDER BY f_name

works

Sorting

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’ORDER BY phone

doesn‘t work

Tip #2:Remember: Index can only benefit the sorting of the column immediately following last column used for search

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT phone FROM PhoneBookWHERE l_name = ‘Kuijpers’AND f_name = ‘Hans’

works

Index – Only Search

Because phone is included in index we can select it without overhead even though we didn’t include it in the search

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT phone FROM PhoneBookWHERE l_name = ‘Kuijpers’AND f_name = ‘Hans’

works

Index – Only Search

Tip #3:Putting columns in index even if they are not part of the query might speed things up – COVERING INDEX

Because phone is included in index we can select it without overhead even though we didn’t include it in the search

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

idx (l_name, f_name, phone)

SELECT * FROM PhoneBookWHERE l_name = ‘Kuijpers’OR f_name = ‘Hans’

doesn‘t work

Disjoint Selector

additional index (f_name, phone) would be required

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerInsurance Company:PolicyNumberAgentType

idx (policy, name, type)

Index Size

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerInsurance Company:PolicyNumberAgentType

idx (policy, name, type)

SELECT * FROM InsuranceCompanyWHERE type = ‘Vervoerdersaansprakelijkheidsverzekering’

Index Size

Type Options: 1. Vervoerdersaansprakelijkheidsverzekering2. Bestuurdersaansprakelijkheidsverzekering3. Overeenstemmingsbeoordelingsprocedures

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerInsurance Company:PolicyNumberAgentType

idx (type(1))

SELECT * FROM InsuranceCompanyWHERE type = ‘Vervoerdersaansprakelijkheidsverzekering’

Index Size

Type Options: 1. Vervoerdersaansprakelijkheidsverzekering2. Bestuurdersaansprakelijkheidsverzekering3. Overeenstemmingsbeoordelingsprocedures

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerInsurance Company:PolicyNumberAgentType

idx (type(1))

SELECT * FROM InsuranceCompanyWHERE type = ‘Vervoerdersaansprakelijkheidsverzekering’

Index Size

Type Options: 1. Vervoerdersaansprakelijkheidsverzekering2. Bestuurdersaansprakelijkheidsverzekering3. Overeenstemmingsbeoordelingsprocedures

Trick #1:Use only part of the column as length of the index, but remember that it will break the ‘covering’ property.

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerInsurance Company:PolicyNumberAgentType

idx (type(1))

SELECT * FROM InsuranceCompanyWHERE type = ‘Vervoerdersaansprakelijkheidsverzekering’

Index Size

Type Options: 1. Vervoerdersaansprakelijkheidsverzekering2. Bestuurdersaansprakelijkheidsverzekering3. Overeenstemmingsbeoordelingsprocedures

Trick #1a:Specificity Check

SELECTCOUNT(DISTINCT(type)) AS total,COUNT(DISTINCT(LEFT(type,10))) AS t10,COUNT(DISTINCT(LEFT(type,20))) AS t20FROM Insurance Company;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerInsurance Company:PolicyNumberAgentType

idx (type(1))

SELECT * FROM InsuranceCompanyWHERE type = ‘Vervoerdersaansprakelijkheidsverzekering’

Index Estimation

Type Options: 1. Vervoerdersaansprakelijkheidsverzekering2. Bestuurdersaansprakelijkheidsverzekering3. Overeenstemmingsbeoordelingsprocedures

Trick #1b:Cardinality

ANALYZE TABLES;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index PrimerPhone Book:Last Name (ASC)First Name (ASC)Phone Number (RAND)

MySQL 5.5

idx (l_name) – traditionalidx (l_name, f_name, phone) – covering

Range Lookup

SELECT phone FROM PhoneBookWHERE l_name = ‘Kuijpers’AND f_name LIKE ‘%Hans%’

MySQL 5.6

idx (l_name, f_name)Range access l_name, Filter clause on f_name (only read if full row match)

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index StrategyChoose Index order which benefits more queries• SELECT * FROM t WHERE a=1 AND b=2• SELECT * FROM t WHERE a>1 AND b=2

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index StrategyChoose Index order which benefits more queries• SELECT * FROM t WHERE a=1 AND b=2• SELECT * FROM t WHERE a>1 AND b=2KEY (b, a) is better than KEY (a, b)

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index Strategy (Trick #2)

Choose Index order which benefits more queries• SELECT * FROM t WHERE a=1 AND b=2• SELECT * FROM t WHERE a>1 AND b=2KEY (b, a) is better than KEY (a, b)

Index order (without restrictions) should be most selective to least selective

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index Strategy (Trick #1)

• SELECT * FROM t WHERE a=1 AND b=2• SELECT * FROM t WHERE a>1 AND b=2KEY (a, b)

• SELECT * FROM t WHERE a BETWEEN 2 AND 4 AND b=2

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index Strategy (Trick #1)

• SELECT * FROM t WHERE a=1 AND b=2• SELECT * FROM t WHERE a>1 AND b=2KEY (a, b)

• SELECT * FROM t WHERE a BETWEEN 2 AND 4 AND b=2KEY (a, b)

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Query Optimization (Trick #1)

• SELECT * FROM t WHERE a=1 AND b=2• SELECT * FROM t WHERE a>1 AND b=2KEY (a, b)

• SELECT * FROM t WHERE a BETWEEN 2 AND 4 AND b=2KEY (a, b)

• SELECT * FROM t WHERE a IN(2,3,4) AND b=2KEY (a, b)

Enumerating Ranges will ensure the usage of both key parts

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Query Optimization (Trick #2)

CREATE TABLE profile( id INT, gender CHAR(1), age TINYINT(3), city VARCHAR(100), PRIMARY KEY(id));

SELECT id FROM profileWHERE city = “Amsterdam”AND age BETWEEN 35 AND 40

KEY (city, age, id)

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Query Optimization (Trick #2)

CREATE TABLE profile( id INT, gender CHAR(1), age TINYINT(3), city VARCHAR(100), PRIMARY KEY(id));

SELECT id FROM profileWHERE city = “Amsterdam”AND age BETWEEN 35 AND 40

KEY (city, age, id)

SELECT id FROM profileWHERE city = “Amsterdam”AND gender = “F”AND age BETWEEN 35 AND 40

KEY (city, gender, age, id)

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Index Strategy (Trick #3)

CREATE TABLE profile( id INT, gender CHAR(1), age TINYINT(3), city VARCHAR(100), PRIMARY KEY(id));

SELECT id FROM profileWHERE city = “Amsterdam”AND age BETWEEN 35 AND 40

KEY (city, gender, age, id)

SELECT id FROM profileWHERE city = “Amsterdam”AND gender = “F”AND age BETWEEN 35 AND 40

KEY (city, gender, age, id)Trick #3:Use as little indexes as possible. A little query rewrite could save massive overhead on the index-side.

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Query Optimization (Trick #2)

SELECT id FROM profileWHERE city = “Amsterdam”AND age BETWEEN 35 AND 40

KEY (city, gender, age, id)

SELECT id FROM profileWHERE city = “Amsterdam”AND gender = “F”AND age BETWEEN 35 AND 40

KEY (city, gender, age, id)

SELECT id FROM profileWHERE city = “Amsterdam”AND gender IN(‘M’,’F’,’O’)AND age IN(35,36,37,38,39,40)

KEY (city, gender, age, id)

SELECT id FROM profileWHERE city = “Amsterdam”AND gender = “F”AND age IN(35,36,37,38,39,40)

KEY (city, gender, age, id)

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling (Trick #1)

SELECT id, name, picture, … , age FROM profileWHERE city = “Amsterdam”AND gender IN(‘M’,’F’,’O’)AND age IN(35,36,37,38,39,40)ORDER BY ratingLIMIT 100, 10

(page 11)

Retrieves 110 rows and discards 100

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling (Trick #1)

SELECT id, name, picture, … , age FROM profileWHERE city = “Amsterdam”AND gender IN(‘M’,’F’,’O’)AND age IN(35,36,37,38,39,40)ORDER BY ratingLIMIT 100, 10

(page 11)

Retrieves 110 rows and discards 100

SELECT id, name, picture, … , ageFROM profile INNER JOIN( SELECT id FROM profile

WHERE city = “Amsterdam”AND gender IN(‘M’,’F’,’O’)AND age IN(35,36,37,38,39,40)ORDER BY ratingLIMIT 100, 10)

AS xUSING (id);

(page 11)

Retrieves 110 ids and discards 100Retrieves only 10 rows of data

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Query Optimization (Trick #3)

SELECT id FROM urlWHERE url="http://www.joomladagen.nl";

CREATE TABLE pseudohash ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, url VARCHAR(255) NOT NULL, url_crc INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY(id));

CREATE TRIGGER pseudohash_crc_ins BEFORE INSERT ON pseudohash FOR EACH ROW BEGIN SET NEW.url_crc=crc32(NEW.url);

CREATE TRIGGER pseudohash_crc_upd BEFORE UPDATE ON pseudohash FOR EACH ROW BEGIN SET NEW.url_crc=crc32(NEW.url);

SELECT id FROM url WHERE url_crc=CRC32("http://www.joomladagen.nl") AND url="http://www.joomladagen.nl";

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Query Optimization (Trick #3)

SELECT id FROM urlWHERE url="http://www.joomladagen.nl";

CREATE TABLE pseudohash ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, url VARCHAR(255) NOT NULL, url_crc INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY(id));

CREATE TRIGGER pseudohash_crc_ins BEFORE INSERT ON pseudohash FOR EACH ROW BEGIN SET NEW.url_crc=crc32(NEW.url);

CREATE TRIGGER pseudohash_crc_upd BEFORE UPDATE ON pseudohash FOR EACH ROW BEGIN SET NEW.url_crc=crc32(NEW.url);

SELECT id FROM url WHERE url_crc=CRC32("http://www.joomladagen.nl") AND url="http://www.joomladagen.nl";

Fixed length index Fixed length data

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling (Trick #2)

SELECT * FROM comment AS c LEFT JOIN article AS a ON a.id_comment = c.id LEFT JOIN users AS u ON a.created_by = u.idWHERE a.title=‘joomladagen‘AND u.name = ‘Hans Kuijpers’;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling (Trick #2)

SELECT * FROM comment AS c LEFT JOIN article AS a ON a.id_comment = c.id LEFT JOIN users AS u ON a.created_by = u.idWHERE a.title=‘joomladagen‘AND u.name = ‘Jisse Reitsma’;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling (Trick #2)

SELECT * FROM comment AS c LEFT JOIN article AS a ON a.id_comment = c.id LEFT JOIN users AS u ON a.created_by = u.idWHERE a.title=‘joomladagen‘AND u.name = ‘Jisse Reitsma’;

CREATE TABLE comment ( id INT(11) NOT NULL…, tag VARCHAR(255)…, importance TINYINT(3)…, image BLOB…, comment TEXT…, created DATETIME…, created_by INT(11)…, modified…, …., …., publish_up DATETIME, PRIMARY KEY(`id`)) ENGINE=InnoDB;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling (Trick #2)

SELECT * FROM comment AS c LEFT JOIN article AS a ON a.id_comment = c.id LEFT JOIN users AS u ON a.created_by = u.idWHERE a.title=‘joomladagen‘AND u.name = ‘Jisse Reitsma’;

CREATE TABLE comment ( id INT(11) NOT NULL…, tag VARCHAR(255)…, importance TINYINT(3)…, image BLOB…, comment TEXT…, created DATETIME…, created_by INT(11)…, modified…, …., …., publish_up DATETIME, PRIMARY KEY(`id`)) ENGINE=InnoDB;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling (Trick #2)

SELECT c.comment FROM comment AS c LEFT JOIN article AS a ON a.id_comment = c.id LEFT JOIN users AS u ON a.created_by = u.idWHERE a.title=‘joomladagen‘AND u.name = ‘Jisse Reitsma’;

CREATE TABLE comment ( id INT(11) NOT NULL…, tag VARCHAR(255)…, importance TINYINT(3)…, image BLOB…, comment TEXT…, created DATETIME…, created_by INT(11)…, modified…, …., …., publish_up DATETIME, PRIMARY KEY(`id`)) ENGINE=InnoDB;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Resource Handling (Trick #2a)

SELECT SUBSTRING(c.comment, 0, 25) AS short_comment FROM comment AS c LEFT JOIN article AS a ON a.id_comment = c.id LEFT JOIN users AS u ON a.created_by = u.idWHERE a.title=‘joomladagen‘AND u.name = ‘Jisse Reitsma’;

CREATE TABLE comment ( id INT(11) NOT NULL…, tag VARCHAR(255)…, importance TINYINT(3)…, image BLOB…, comment TEXT…, created DATETIME…, created_by INT(11)…, modified…, …., …., publish_up DATETIME, PRIMARY KEY(`id`)) ENGINE=InnoDB;

Eli Aschkenasy - @EliAschkenasy - #JDNL16

Query Optimization (Trick #4)

SELECT SUBSTRING(c.comment, 0, 25) AS short_comment FROM comment AS c LEFT JOIN article AS a ON a.id_comment = c.id LEFT JOIN users AS u ON a.created_by = u.idWHERE a.title=‘joomladagen‘AND u.name = ‘Jisse Reitsma’;

SELECT id FROM usersWHERE name = ‘Jisse Reitsma’; 57125

SELECT id FROM articleWHERE title = ‘Joomladagen’AND created_by = 57125; (123,223,934,1145)

SELECT SUBSTRING(comment, 0, 25) AS short_comment FROM commentWHERE id IN(123,223,934,1145);

Trick #4:Query cache validation of 3 tables.Complex queries reduce likelihood of repetition.

Eli Aschkenasy - @EliAschkenasy - #JDNL16

THANK YOU

Eli Aschkenasy - @EliAschkenasy - #JDNL16

MySQL 5.7 – JSON CREATE TABLE profile( id INT(11)…, profile TEXT…, PRIMARY KEY (`id`)) ENGINE = InnoDB;

INSERT INTO profile (id, profile)VALUES (1, ‘{“handle”: “@EliAschkenasy”}’);

Eli Aschkenasy - @EliAschkenasy - #JDNL16

MySQL 5.7 – JSON CREATE TABLE profile( id INT(11)…, profile TEXT…, PRIMARY KEY (`id`)) ENGINE = InnoDB;

INSERT INTO profile (id, profile)VALUES (1, ‘{“handle”: “@EliAschkenasy”}’);

SELECT id FROM profileWHERE field_name REGEXP '"handle":"([^"]*)@EliAschkenasy([^"]*)"';

Eli Aschkenasy - @EliAschkenasy - #JDNL16

MySQL 5.7 – JSON CREATE TABLE profile( id INT(11)…, profile TEXT…, PRIMARY KEY (`id`)) ENGINE = InnoDB;

INSERT INTO profile (id, profile)VALUES (1, ‘{“handle”: “@EliAschkenasy”}’);

SELECT id FROM profileWHERE field_name REGEXP '"handle":"([^"]*)@EliAschkenasy([^"]*)"';

Full Table Scan (not cacheable just like full text search)

REGEX

Eli Aschkenasy - @EliAschkenasy - #JDNL16

MySQL 5.7 – JSON CREATE TABLE profile( id INT(11)…, profile JSON, PRIMARY KEY (`id`)) ENGINE = InnoDB;

INSERT INTO profile (id, profile)VALUES (1, ‘{“id”: 1, “handle”: “@EliAschkenasy”}’);

SELECT JSON_EXTRACT(profile, ‘$.handle’) FROM profileWHERE id = 1; - @EliAschkenasy

Eli Aschkenasy - @EliAschkenasy - #JDNL16

MySQL 5.7 – JSON CREATE TABLE profile( id INT(11)…, profile JSON, PRIMARY KEY (`id`)) ENGINE = InnoDB;

INSERT INTO profile (id, profile)VALUES (1, ‘{“id”: 1, “handle”: “@EliAschkenasy”}’);

SELECT JSON_EXTRACT(profile, ‘$.handle’) FROM profileWHERE id = 1; - @EliAschkenasy

JSON data type consumes about 5% more memory JSON columns can’t have a default value JSON columns can’t be indexed BETWEEN, IN(), GREATEST(), LEAST() Are NOT supported yet!

top related