0

I have 3 enum() columns with the same option values inside.
I first tried to use the "set" datatype which originally was meant to do that (hold multiple values from a set) but it seems that datatype isn't managed for 15+ years and isn't even supporting an index.
Is there a nice way to index those 3 columns so I can use them in searched without destroying query performance ?

SELECT * FROM TABLE WHERE a='x' OR b='x' OR c='x'

I thought about creating a virtual field which uses a boolean logic (&) on the 3 enum field-numbers and combines them into a large number but that's quite a hack and not nice to maintain.

Has someone solved this sort of task elegantly ? (I do not want to use a support table and JOIN it, I want to stay with a single table)

1
  • Slightly off topic, but` 'x' in (a, b, c)` Is a bit shorter. Commented Dec 13, 2020 at 5:05

3 Answers 3

1

UNION

You have listed most of the approaches. Here's one more:

SELECT ... WHERE a='x'
UNION ALL
SELECT ... WHERE b='x'
UNION ALL
SELECT ... WHERE c='x'

And have separate (not composite) indexes:

INDEX(a), INDEX(b), INDEX(c)

Each SELECT will efficiently use one of the indexes. Then the UNION will combine the results.

Use UNION DISTINCT if you expect dups and need to remove them.

that datatype isn't managed for 15+ years

Yeah, SET has been stable for a long time (more than 20 years). I tend to use some sized INT together with boolean operators instead of SET. But a table scan is needed in either case.

How big is your table? 1000 rows is hardly worth worrying about performance on.

FULLTEXT

Even faster (in some situations):

FULLTEXT(a, b, c)

WHERE MATCH(a, b, c) AGAINST('+x' IN BOOLEAN MODE)

(No OR, no UNION, etc.) If Fulltext is applicable, this will be very fast.

2
  • 1.5 billion rows ;/
    – John
    Commented Dec 18, 2020 at 2:59
  • @John -- OK, there's another way.
    – Rick James
    Commented Dec 18, 2020 at 3:29
0

Because you need to use OR logic in your WHERE predicate, there's not a way to create one index that covers every case unfortunately.

Fortunately you're basically on the money with a virtual field or rather you should create a generated column, and then you can create an index on that single column so your queries are performant.

1
  • Yes, I'm quite sure there is a way to generate a generated column though I've not seen examples of that anywhere. Given that 1 enum seems to have a 16 bit key a 64 bit number can hold 4 enums, so with some shifting it should be posible to create 1 large number out of my 3 enums. Though I don't exactly know how to do it in mysql, also querying for it sounds difficult I've the feeling this will be a major pain to maintain
    – John
    Commented Dec 18, 2020 at 3:00
0

There's a reason why set isn't managed anymore/not supporting an index: It's essentially just a bitwise representation for a set to minimize memory usage.

However, an index on a set column supporting performant set operations would require multiple indexes in the background for each every, as if one would just make a boolean column for each possible set entry and then made a separate index per column (to make checking if a value is in the set fast), and also possibly other indexes of column combinations.

All in all this would no longer be space efficient and the expected performance for filtering by the set/writing to the column would be easily comprehensible.

You're better off making separate boolean columns or another table with 1:n relationship and define the indexes the way you need them because a catch all set index would be too costly to maintain in most cases

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.