DBAL cardinality violation error
Asked Answered
I

2

5

I am getting the 'Cardinality Violation' error, for the following SQL:

Doctrine\DBAL\Exception\DriverException: An exception occurred while executing

SELECT p.* FROM mod_products_products p 
LEFT JOIN mod_products_products_categories c_link ON c_link.product_id = p.id 
LEFT JOIN mod_products_brands b ON p.brand_id = b.id 
LEFT JOIN mod_products_groups vg ON p.variation_id = vg.id 
LEFT JOIN mod_products_categories c ON c_link.category_id = c.id 
LEFT JOIN mod_products_group_options vg_o ON vg_o.group_id = vg.id 
LEFT JOIN mod_products_group_values vg_o_v ON vg_o_v.option_id = vg_o.id 
WHERE (p.name LIKE (?, ?)) AND (p.parent_id = 0) AND (vg_o.disabled=0) 
GROUP BY p.id ORDER BY p.name ASC 
LIMIT 18446744073709551615 OFFSET 0

with params ["%big%", "%light%"]: SQLSTATE[21000]: Cardinality violation: 1241 Operand should contain 1 column(s).

The error only occurs if there is more than one value defined in the parameter list for WHERE (p.name LIKE (?, ?)).

I am using executeQuery(), and passing the array as Connection::PARAM_STR_ARRAY. In the original statement I am defining the trouble point as:

$builder->andWhere('p.name LIKE (:partial_names)');

It seems it doesn't like getting an array passed as partial_names. Any ideas on what is causing this, and how to avoid it?

Impress answered 13/6, 2016 at 9:39 Comment(3)
See also https://mcmap.net/q/82454/-mysql-like-inHamish
What made you think that mysql LIKE can accept more than one argument?Maldonado
@YourCommonSense Since foo LIKE ('bar') is valid in MySQL, I think it's natural to think foo LIKE ('bar', 'baz') would also be valid.Hamish
H
8

MySQL LIKE is a "string comparison function" and as such compares one string to another, using "simple pattern matching".

If you check the SQL standard, you'll notice that the BNF grammar for LIKE accepts only "character-like" and "octet-like" arguments, both of which are essentially what we'd call strings. (There is some detail around the fact that LIKE performs a binary, character-for-character match on the RHS, which is different than how = operates: foo LIKE 'bar' and foo='bar' may produce different results.)

All this means you can't do LIKE ('a', 'b') because the columnar expression ('a', 'b') is not string-like. Or in geeky standard language, it's cardinality (2) differs from the expected cardinality (1). However, you can do this in MySQL and SQLite (maybe other engines):

WHERE foo LIKE ('%bar')

because the cardinality of the RHS is 1 (there is one column), which is what LIKE expects.

You're wanting something effectively similar to foo LIKE IN ('a', 'b'), but that doesn't exist either (for the SQL standard reason mentioned above). This Q&A shows some workarounds for that behavior, REGEXP based being the accepted answer.

So, to get around this error, you need to rewrite your query to use multiple LIKE, or a REGEXP, or maybe even something like FIND_IN_SET.

Hamish answered 23/6, 2016 at 13:36 Comment(0)
T
2

Change

(p.name LIKE (?, ?))

to

(p.name LIKE ? OR p.name LIKE ?)

and

["%big%", "%light%"]

to

"%big%", "%light%"
Tahiti answered 24/6, 2016 at 15:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.