10

Is there a function like "unnest" from POSTGRESQL on MYSQL?

Query (PSQL):

select unnest('{1,2,3,4}'::int[])

Result (as table):

 int |
_____|
  1  |
_____|
  2  |
_____|
  3  |
_____|
  4  |
_____|
Phillip Parente
  • 165
  • 1
  • 1
  • 8
  • 3
    This is not possible in MySQL. Neither with built-in functions nor can you write a function to mimic this behaviour. –  Dec 08 '14 at 19:16
  • 3
    @PhillipParente You'll get better answers/comments if you indicate what you tried for yourself first, and if you explain what you're trying to achieve. "I didn't find anything suitable in [the manual]", ... "I'm trying to supply a set of rows as a string and want to have the database expand it", etc. In this case nobody can (without lots of guessing) say "No, but have you considered..." because you haven't told us *why* you're trying to do this, what the underlying problem you're trying to solve is. – Craig Ringer Dec 08 '14 at 19:32
  • 2
    "I want to select a array like rows…" — MySQL doesn't have an array type. What values/types do you have instead; a specially formatted string perhaps? – salathe Dec 08 '14 at 20:00
  • I was trying to do the difference between a csv list of strings and what I have in database. `[(csv) A B c d ] [(DATABASE) B C ] [(Result) A d ]` – Phillip Parente Dec 12 '14 at 20:19

2 Answers2

7

Short answer

Yes, it is possible. From technical viewpoint, you can achieve that with one query. But the thing is - most probably, you are trying to pass some logic from application to data storage. Data storage is intended to store data, not to represent/format it or, even more, apply some logic to it.

Yes, MySQL doesn't have arrays data type, but in most cases it won't be a problem and architecture can be created so it will fit those limitations. And in any case, even if you'll achieve it somehow (like - see below) - you won't be possible to properly work later with that data, since it will be just result set. You may store it, of course - so to, let's say, index later, but then it's again a task for an application - so to create that import.

Also, make sure that it is not a Jaywalker case, so not about storing delimiter-separated values and later trying to extract them.

Long answer

From technical viewpoint, you can do it with Cartesian product of the two row sets. Then use a well known formula:

N = d1x101 + d2x102 + ...

Thus, you'll be able to create a "all-numbers" table and later iterate through it. That iteration, together with MySQL string functions, may lead you to something like this:

SELECT 
  data 
FROM (
  SELECT 
    @next:=LOCATE(@separator,@search, @current+1) AS next, 
    SUBSTR(SUBSTR(@search, @current, @next-@current), @length+1) AS data, 
    @next:=IF(@next, @next, NULL) AS marker, 
    @current:=@next AS current 
  FROM 
    (SELECT 0 as i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as n1    
    CROSS JOIN 
    (SELECT 0 as i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as n2 
    CROSS JOIN 
    (SELECT 
       -- set your separator here:
       @separator := ',', 
       -- set your string here:
       @data      := '1,25,42,71',
       -- and do not touch here:
       @current   := 1,
       @search    := CONCAT(@separator, @data, @separator), 
       @length    := CHAR_LENGTH(@separator)) AS init
    ) AS joins 
WHERE 
  marker IS NOT NULL

The corresponding fiddle would be here.

You should also notice: this is not a function. And with functions (I mean, user-defined with CREATE FUNCTION statement) it's impossible to get result row set since function in MySQL can not return result set by definition. However, it's not true to say that it's completely impossible to perform requested transformation with MySQL.

But remember: if you are able to do something, that doesn't mean you should do it.

Alma Do
  • 37,009
  • 9
  • 76
  • 105
  • Thank you @Alma Do! I was trying to do the difference between a csv list of strings and what I have in database. `[(csv) A B c d ] [(DATABASE) B C ] [(Result) A d ] ` – Phillip Parente Dec 12 '14 at 20:07
  • "Of course" (because the 2 cross joins between 0-9), this doesn't work if there are more than 100 elements. I suppose you just have to add a "(SELECT 0 ... ) CROSS JOIN " line for each power of 10 you want. But it supposes you know a max count of your data. – Pierre-Olivier Vares Nov 09 '18 at 17:04
  • You will need to add more members to a sum. 3 for 1.000, 4 for 10.000 and so on (see explanations). However I'd still urge against using this approach. It is shown only of academic interest – Alma Do Nov 09 '18 at 18:09
  • 1
    So sad to know this when you are using MySQL for about 1 year and planning to use a column with comma separated values. I really miss Postgres... – Diego Victor de Jesus Mar 03 '20 at 21:18
  • " Data storage is intended to store data, not to represent/format it or, even more, apply some logic to it. " Why would RDBMS have so much formatting and transformational power if we weren't intended to use it? That's akin to suggesting we should use ferraris like suitcases and only fill their trunks... RDBMS let us work with data *declaratively* rather than *imperatively* in application logic. – kevlarr Jul 23 '21 at 18:51
2

This sample fetchs all "catchwords" from Table data, wich are seperated by "," Maximum values in the commaseparated list is 100

    WITH RECURSIVE num (n) AS (
      SELECT  1
      UNION ALL
      SELECT n+1 FROM num WHERE n<100 -- change this, if more than 100 elements
    )
    SELECT DISTINCT substring_index(substring_index(catchwords, ',', n), ',', -1) as value
    FROM data
    JOIN num
      ON char_length(catchwords) - char_length(replace(catchwords, ',', '')) >= n - 1

In newer Version of MySQL/MariaDB you can use JSON_TABLE if you can JOIN the elements:

SELECT cat.catchword, dat.* 
FROM data dat 
CROSS JOIN json_table(concat('[',dat.catchwords, ']')
                    , '$[*]' COLUMNS(
                               catchword VARCHAR(50) PATH '$'
                             )
) AS words 
Frank
  • 1,901
  • 20
  • 27