Re: How to enforce a specific order of group_concat?

Jean-Luc Hainaut <[email protected]> Mon, 02 Mar 2020 15:29:10 +0100
Newsgroups gmane.comp.db.sqlite.general
Message-ID <[email protected]>
On 1/03/2020 22:57, mailing lists wrote:
> Assume I create the following table:
>
> CREATE TABLE Test (ID INTEGER PRIMARY KEY, Value TEXT);
> INSERT INTO Test (Value) VALUES('Alpha');
> INSERT INTO Test (Value) VALUES('Beta');
> INSERT INTO Test (Value) VALUES('Beta');
> INSERT INTO Test (Value) VALUES('Alpha');
>
> According to the documentation of group_concat the order is undefined, indeed:
>
> SELECT group_concat(DISTINCT Value) FROM Test ORDER BY Value ASC;
> SELECT group_concat(DISTINCT Value) FROM Test ORDER BY Value DESC;
>
> Both queries result in Alpha,Beta.
>
> Changing the queries to
>
> WITH Result(x) AS (SELECT DISTINCT Value FROM Test ORDER BY Value ASC) SELECT group_concat(x) FROM Result;
> WITH Result(x) AS (SELECT DISTINCT Value FROM Test ORDER BY Value DESC) SELECT group_concat(x) FROM Result;
>
> leads to the results Alpha,Beta, respectively Beta,Alpha.
>
> Is this a coincidence or is this guaranteed to work?
>
> Are there any other solutions / possibilities?

I also sometimes need a deterministic version of group_concat(). For 
such cases, I have written the short UDF below (in Python, but I guess 
the algorithm can easily be translated in other languages):

JLH

class group_concat2:
     # Rewriting of "group_concat" of SQLite to simulate that of MySQL.
     # Implements "distinct", "order by", "descending" and "separator".
     # Interprets "null" values "intuitively"
     #
     # Format: group_concat2(value,distinct,sortkey,direction,separator)
     #   value:     char or numeric SQL expression; if numeric, 
converted into char;
     #              the next value to concatenate;
     #              discarded if None (Python translation of SQL null).
     #   distinct:  numeric or char SQL expression; if char, converted 
into integer;
     #              uniqueness indicator;
     #              if 1, duplicates ignored; if 0, duplicates allowed.
     #   sortkey:   char or numeric SQL expression (no conversion);
     #              the order key value for the current "value" instance;
     #              If None or u'', the current "value" instance is used 
instead.
     #   direction: numeric or char SQL expression; if char, converted 
into integer;
     #              ordering direction (1 = asc; 2 = desc).
     #   sep:       char or numeric SQL expression; if numeric, 
converted into char;
     #              value separator;
     #              If None, = default u','.
     # Example:
     #   select City,group_concat2(lower(CustID),1,Account,'2','; ') as 
Customers
     #   from   CUSTOMER group by City;

     def __init__(self):
         # Initialize
         self.number  = 0    # number of values added
         self.valList = []   # List of values to concatenate
         self.orderby = []   # list of values of the order key
         self.distinct = 0   # whether "valList" values must be unique 
(0 = no; 1 = yes)
         self.direction = 1  # ordering direction (1 = asc; 2 = desc)
         self.sep = u','     # separator

     def step(self,value,distinct,sortkey,direction,sep):
         # Adding a new value to concatenate.
         # Each call of this method may specify different values of
         #      (distinct,sortkey,direction,sep) parameters.
         # However, only those specified by the call of the first 
"value" instance
         # will be considered, the others being ignored.

         import numbers

         self.number += 1

         # Initialize user values of "distinct", "direction" and "sep"
         if self.number == 1:
             if distinct in [1,u'1']:
                 self.distinct = 1
             if direction in [1,2,u'1',u'2']:
                 self.direction = int(direction)
             if sep is not None:
                 if isinstance(sep,numbers.Number):
                     self.sep = unicode(sep)
                 else:
                     self.sep = sep

         if sortkey is None:
             sortkey = value if value is not None else 1
         elif sortkey == u'':
             sortkey = value if value is not None else 1

         if value is not None:
             if isinstance(value,numbers.Number):
                 value = unicode(value)

             if self.distinct:
                 if value not in self.valList:
                     self.valList.append(value)
                     self.orderby.append(sortkey)
             else:
                 self.valList.append(value)
                 self.orderby.append(sortkey)
         else:
             # value discarded
             pass

     def finalize(self):
         if self.direction == 1:
             self.valList = [y for x,y in 
sorted(zip(self.orderby,self.valList),reverse=False)]
         else:
             self.valList = [y for x,y in 
sorted(zip(self.orderby,self.valList),reverse=True)]
         return self.sep.join(self.valList)








_______________________________________________
sqlite-users mailing list
[email protected]
http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users