mariadb-syntax-window-and-cte
Installation
SKILL.md
mariadb-syntax-window-and-cte
Window functions and Common Table Expressions in MariaDB 10.6 LTS, 10.11 LTS, 11.x, and 12.x. All claims verified against mariadb.com/kb/en/window-functions-overview/, window-frames/, with/, and recursive-common-table-expressions-overview/ on 2026-05-19.
Quick Reference
- ALWAYS use
ROWSorRANGEfor frame units. NEVER useGROUPS: MariaDB does NOT implement it at any version through 12.x (PostgreSQL and MySQL 8.0+ only). - ALWAYS write an explicit frame for
LAST_VALUE,FIRST_VALUE,NTH_VALUE,SUM,AVG,COUNT,MIN,MAXwhen you want partition-wide results. The default frame whenORDER BYis present isRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(a running window, NOT the full partition). - ALWAYS terminate a recursive CTE with a depth guard (
WHERE depth < N) orCYCLE col RESTRICT(10.5.2+). Without termination,max_recursive_iterations(default 1000) aborts the query with error 1969. - ALWAYS write
ORDER BYinsideOVER()when using value functions (LAG,LEAD,FIRST_VALUE,LAST_VALUE,NTH_VALUE). They have no defined behavior without it. - ALWAYS use
ROW_NUMBER()(notRANK) when you need a unique rank per row.RANKskips after ties (1, 1, 3, 4) ;DENSE_RANKdoes not skip (1, 1, 2, 3) ;ROW_NUMBERis always unique. - NEVER assume a non-recursive CTE is materialized. The optimizer merges it into the outer query when
optimizer_switch=derived_merge=on(the default). UseEXPLAINto confirm :select_type=DERIVEDmeans materialized, inline expansion means merged. Recursive CTEs are ALWAYS materialized. - NEVER use
EXCLUDE CURRENT ROW,EXCLUDE GROUP,EXCLUDE TIES,EXCLUDE NO OTHERS, orDISTINCTinside an aggregate-as-window. None are implemented in MariaDB. - NEVER use
NULLS FIRSTorNULLS LASTinORDER BYinsideOVER(): not supported.NULLsorts first underASC, last underDESC(the default). - ALWAYS check version : recursive CTE needs 10.2.2+,
CYCLE col RESTRICTneeds 10.5.2+,WITHIN GROUPforPERCENTILE_CONT/PERCENTILE_DISC/MEDIANneeds 10.3.3+.