[sqlite] "Circular" order by

2016-03-11 Thread Kees Nuyt
On Fri, 11 Mar 2016 09:05:27 +0200, R Smith  wrote:

>
>On 2016/03/11 5:52 AM, Stephen Chrzanowski wrote:
>> On Thu, Mar 10, 2016 at 2:16 PM, R Smith  wrote:
>>
>>
>>> I do this kind of thing so often when filling a selection box for instance:
>>>SELECT 'None'
>>> UNION ALL
>>>SELECT City FROM Countrylist WHERE Country = :1
>>> UNION ALL
>>> SELECT City FROM Countrylist WHERE Country <> :1 ORDER BY City
>>>
>>> Which, as you can deduce, adds a 'None' to the option list, then the
>>> selected country's capital city, then the other cities in alphabetical
>>> order. I now think I need a more sophisticated method to ensure that output
>>> doesn't get mangled. If SQLite ever changes this behaviour, lots of things
>>> will break for me, but, that's life, I will start fixing them all.
>>>
>>> Heh, assumptions... that'll teach me! :)
>>> Ryan
>>>
>> Not that I want to hijack the thread, but with the country list I got from
>> here:
>> https://raw.githubusercontent.com/umpirsky/country-list/master/data/en/country.sqlite.sql
>>
>> I came up with this simple modification to your query:
>>
>> SELECT 'None',0 as OrderNum
>> UNION ALL
>>SELECT Value,1  FROM List WHERE Value = :1
>> UNION ALL
>>SELECT Value,2 FROM List WHERE Value <> :1 ORDER BY OrderNum,Value
>
>Yeah, though I think this one might still be in danger of not ending up 
>in the correct order since the UNIONed section, although able to order, 
>doesn't guarantee order, nor influences the order in the other UNIONed 
>sections and output as a whole... a bit weird, but understandable. So 
>the foolproof way I think would be adapting your advice into this:
>
>SELECT V FROM (
> SELECT 'None' AS V, 0 as Ord
>   UNION ALL
> SELECT Value,1 FROM List WHERE SomeIndex == :1
>   UNION ALL
> SELECT Value,2 FROM List WHERE SomeIndex <> :1
>) ORDER BY Ord, V

That is not needed, as http://www.sqlite.org/lang_select.html#orderby
says:

"The ORDER BY clause

[]

In a compound SELECT statement, only the last or right-most simple
SELECT may have an ORDER BY clause. That ORDER BY clause will apply
across all elements of the compound. If the right-most element of a
compound SELECT is a VALUES clause, then no ORDER BY clause is allowed
on that statement."

> Easy enough, but alas!, the amount of places I have to go and change... :)

HTH ;)

-- 
Regards,
Kees Nuyt


[sqlite] "Circular" order by

2016-03-11 Thread R Smith


On 2016/03/11 5:52 AM, Stephen Chrzanowski wrote:
> On Thu, Mar 10, 2016 at 2:16 PM, R Smith  wrote:
>
>
>> I do this kind of thing so often when filling a selection box for instance:
>>SELECT 'None'
>> UNION ALL
>>SELECT City FROM Countrylist WHERE Country = :1
>> UNION ALL
>> SELECT City FROM Countrylist WHERE Country <> :1 ORDER BY City
>>
>> Which, as you can deduce, adds a 'None' to the option list, then the
>> selected country's capital city, then the other cities in alphabetical
>> order. I now think I need a more sophisticated method to ensure that output
>> doesn't get mangled. If SQLite ever changes this behaviour, lots of things
>> will break for me, but, that's life, I will start fixing them all.
>>
>> Heh, assumptions... that'll teach me! :)
>> Ryan
>>
> Not that I want to hijack the thread, but with the country list I got from
> here:
> https://raw.githubusercontent.com/umpirsky/country-list/master/data/en/country.sqlite.sql
>
> I came up with this simple modification to your query:
>
> SELECT 'None',0 as OrderNum
> UNION ALL
>SELECT Value,1  FROM List WHERE Value = :1
> UNION ALL
>SELECT Value,2 FROM List WHERE Value <> :1 ORDER BY OrderNum,Value

Yeah, though I think this one might still be in danger of not ending up 
in the correct order since the UNIONed section, although able to order, 
doesn't guarantee order, nor influences the order in the other UNIONed 
sections and output as a whole... a bit weird, but understandable. So 
the foolproof way I think would be adapting your advice into this:

SELECT V FROM (
  SELECT 'None' AS V, 0 as Ord
UNION ALL
  SELECT Value,1 FROM List WHERE SomeIndex == :1
UNION ALL
  SELECT Value,2 FROM List WHERE SomeIndex <> :1
) ORDER BY Ord, V


Easy enough, but alas!, the amount of places I have to go and change... :)

Thanks,
Ryan



[sqlite] "Circular" order by

2016-03-10 Thread Stephen Chrzanowski
On Thu, Mar 10, 2016 at 2:16 PM, R Smith  wrote:


>
> I do this kind of thing so often when filling a selection box for instance:
>   SELECT 'None'
> UNION ALL
>   SELECT City FROM Countrylist WHERE Country = :1
> UNION ALL
> SELECT City FROM Countrylist WHERE Country <> :1 ORDER BY City
>
> Which, as you can deduce, adds a 'None' to the option list, then the
> selected country's capital city, then the other cities in alphabetical
> order. I now think I need a more sophisticated method to ensure that output
> doesn't get mangled. If SQLite ever changes this behaviour, lots of things
> will break for me, but, that's life, I will start fixing them all.
>
> Heh, assumptions... that'll teach me! :)
> Ryan
>

Not that I want to hijack the thread, but with the country list I got from
here:
https://raw.githubusercontent.com/umpirsky/country-list/master/data/en/country.sqlite.sql

I came up with this simple modification to your query:

SELECT 'None',0 as OrderNum
UNION ALL
  SELECT Value,1  FROM List WHERE Value = :1
UNION ALL
  SELECT Value,2 FROM List WHERE Value <> :1 ORDER BY OrderNum,Value


[sqlite] "Circular" order by

2016-03-10 Thread Alberto Wu
On 03/10/16 20:16, R Smith wrote:
>> Hmm, does this work any better?
>>
>> SELECT id FROM t
>> ORDER BY id < 'pen' desc, id;
> 
> It works, but not better.

Indeed. Any operation in ORDER BY (even things like "ORDER BY id||''")
result in scan + temp b-tree.

Thanks again.

-- Alberto


[sqlite] "Circular" order by

2016-03-10 Thread R Smith


On 2016/03/10 8:37 PM, James K. Lowden wrote:
> On Thu, 10 Mar 2016 10:17:57 +0100
> Alberto Wu  wrote:
>
>> On 03/09/16 23:30, James K. Lowden wrote:
 SELECT P.id FROM (
SELECT 0 AS sect, id FROM t WHERE id >= 'pen'
UNION ALL
 SELECT 1, id FROM t WHERE id < 'pen'
 ) AS P
 ORDER BY P.sect, P.id
 ;
>>> This is the correct answer.
>> Hi,
>>
>> unfortunately the correct answer comes with an extra scan and a temp
>> b-tree so I'd rather keep the two queries split and handle the case
>> programmatically.
> Hmm, does this work any better?
>
>   SELECT id FROM t
>   ORDER BY id < 'pen' desc, id;

It works, but not better. I think it was Igor who proposed similar (if 
not, apologies) which of course produces the correct result, but cannot 
take advantage of the index on id so it becomes a result-set walk 
causing longer ordering of values - exactly what the OP tried to avoid.

I myself is sad to find that the SELECTs in between UNION ALL statements 
qualify as sub-selects and do not implicitly honor the order in which 
they are UINIONed - I had this wrong.

I do this kind of thing so often when filling a selection box for instance:
   SELECT 'None'
UNION ALL
   SELECT City FROM Countrylist WHERE Country = :1
UNION ALL
SELECT City FROM Countrylist WHERE Country <> :1 ORDER BY City

Which, as you can deduce, adds a 'None' to the option list, then the 
selected country's capital city, then the other cities in alphabetical 
order. I now think I need a more sophisticated method to ensure that 
output doesn't get mangled. If SQLite ever changes this behaviour, lots 
of things will break for me, but, that's life, I will start fixing them all.

Heh, assumptions... that'll teach me! :)
Ryan





[sqlite] "Circular" order by

2016-03-10 Thread James K. Lowden
On Thu, 10 Mar 2016 21:16:28 +0200
R Smith  wrote:

> > Hmm, does this work any better?
> >
> > SELECT id FROM t
> > ORDER BY id < 'pen' desc, id;
> 
> It works, but not better. I think it was Igor who proposed similar
> (if not, apologies) which of course produces the correct result, but
> cannot take advantage of the index on id so it becomes a result-set
> walk causing longer ordering of values - exactly what the OP tried to
> avoid.

Hmm, I don't know about "cannot", but I'm not surprised by "does not",
because it's a tough inference.  

If you stand back a minute, you can see that

id < 'pen'

is a monotonic function of "id" if "id" is sorted.  The query processor
*could* include that logic, and could choose to process the rows, in
index order, starting with the first row where  id >= 'pen'  , to the
end, and wrapping back to the beginning.  

The big boys do that kind of thing.  In general "order by f(x)" will
use an index on x if f(x) has the same order.  For hard problems, they
support computed columns -- a little like a view attached to a table --
and indexes on them.

In SQLite, I guess the OP's only solution is to make an index of the
kind he needs.  

create table idx as select id < 'pen' as 'LT_pen', id from t;
select id from idx order by LT_pen;

Of course, that presupposes 'pen' is a constant.  Whether or not that's
true wasn't mentioned in the original post.  

--jkl



[sqlite] "Circular" order by

2016-03-10 Thread James K. Lowden
On Thu, 10 Mar 2016 10:17:57 +0100
Alberto Wu  wrote:

> On 03/09/16 23:30, James K. Lowden wrote:
> >> SELECT P.id FROM (
> >>   SELECT 0 AS sect, id FROM t WHERE id >= 'pen'
> >>   UNION ALL
> >> SELECT 1, id FROM t WHERE id < 'pen'
> >> ) AS P
> >> ORDER BY P.sect, P.id
> >> ;
> > 
> > This is the correct answer.  
> 
> Hi,
> 
> unfortunately the correct answer comes with an extra scan and a temp
> b-tree so I'd rather keep the two queries split and handle the case
> programmatically.

Hmm, does this work any better?  

SELECT id FROM t
ORDER BY id < 'pen' desc, id;

--jkl


[sqlite] "Circular" order by

2016-03-10 Thread Alberto Wu
On 03/09/16 23:30, James K. Lowden wrote:
>> SELECT P.id FROM (
>>   SELECT 0 AS sect, id FROM t WHERE id >= 'pen'
>>   UNION ALL
>> SELECT 1, id FROM t WHERE id < 'pen'
>> ) AS P
>> ORDER BY P.sect, P.id
>> ;
> 
> This is the correct answer.  

Hi,

unfortunately the correct answer comes with an extra scan and a temp
b-tree so I'd rather keep the two queries split and handle the case
programmatically.

Thanks a lot for your help.

-- Alberto


[sqlite] "Circular" order by

2016-03-09 Thread R Smith


On 2016/03/09 8:08 PM, Alberto Wu wrote:
> On 03/09/16 17:26, R Smith wrote:
>> Firstly, this is the best method - one I would use. UNION ALL is quite
>> efficient.
>> Secondly, the order by will be  honoured - you can refer to the SQL
>> standard for that even, it's an axiom of the output and probably not
>> even considered "needed" to mention.
> Ryan,
> Thanks a lot for the quick reply.
>
> I had thought about the SQL axiom too.
> However I've also noticed that sqlite actively forbids ORDER BY clauses
> in each SELECT when using UNION / UNION ALL, which makes that assumption
> kind of dull.
> In fact in order to achieve this per-SELECT sorting rather than the
> sorting of the combined result set you have to trick sqlite through the
> use of the WITH construct or via a subquery in FROM (like in my example).
> So in the end I thought I'd just be safe and ask :)

Quite right, apologies, I meant to say that your example was the correct 
way and expected to work, not the initial assumption (which already is 
obvious to not work in SQLite).
i.e. this:

SELECT * FROM (SELECT * FROM t WHERE id >= 'pen' ORDER BY id)
UNION ALL
SELECT * FROM (SELECT * FROM t WHERE id < 'pen' ORDER BY id)
;

As long as you don't impose another order-by on an outer query, nor 
impose the UNION (without the ALL)[1], the output is simply appended and 
will be correctly sorted.

As a point of interest, is this query really much slower for you? It 
should produce the exact same order:
SELECT 0 AS Sect, * FROM t WHERE id >= 'pen'
UNION
SELECT 1, * FROM t WHERE id < 'pen'
;

You could also wrap the above inside another query, keeping the UNION 
ALL, stripping the Sect field and doing your own Order by, like so:

SELECT P.id FROM (
  SELECT 0 AS sect, id FROM t WHERE id >= 'pen'
  UNION ALL
SELECT 1, id FROM t WHERE id < 'pen'
) AS P
ORDER BY P.sect, P.id
;

Test to find the fastest case :)

Cheers!
Ryan


[1] - When using UNION only, SQLite might re-order the output set to be 
able to quicker check for duplications




[sqlite] "Circular" order by

2016-03-09 Thread Alberto Wu
On 03/09/16 17:26, R Smith wrote:
> Firstly, this is the best method - one I would use. UNION ALL is quite
> efficient.
> Secondly, the order by will be  honoured - you can refer to the SQL
> standard for that even, it's an axiom of the output and probably not
> even considered "needed" to mention.

Ryan,
Thanks a lot for the quick reply.

I had thought about the SQL axiom too.
However I've also noticed that sqlite actively forbids ORDER BY clauses
in each SELECT when using UNION / UNION ALL, which makes that assumption
kind of dull.
In fact in order to achieve this per-SELECT sorting rather than the
sorting of the combined result set you have to trick sqlite through the
use of the WITH construct or via a subquery in FROM (like in my example).
So in the end I thought I'd just be safe and ask :)

> A point that might be worth considering: Union all will not rid
> duplicates and it's probably possible to get results like:

That's perfectly fine: like in my example the column is unique in my
real life case as well.
Besides, UNION does not honour the original like UNION ALL does.

> If there are two legitimate copies of pen in your input data and another
> containing a space at the end and you use the RTRIM collation. That may
> or may not be what you want, but should be very easy to deal with either
> way.

The actual column type is BINARY but thanks for the tip anyway!

Thanks again,
-- Alberto


[sqlite] "Circular" order by

2016-03-09 Thread R Smith


On 2016/03/09 6:07 PM, Alberto Wu wrote:
> Hi all,
>
> I'm looking for suggestions...
> What I want to achieve is to "roll" the result set of a query around by
> a certain amount (as in offset + wrap around).
>
> For example, given that:
> CREATE TABLE t (id TEXT NOT NULL PRIMARY KEY);
> INSERT INTO t VALUES ('pen'), ('tree'), ('desk'), ('car');
>
> I would like to have the same result set of:
> SELECT * FROM t ORDER BY id ASC;
> car
> desk
> pen
> tree
>
> Except I would like it to start at "pen", producing:
> pen
> tree
> car
> desk
>
> Now, one way to do it is:
> SELECT * FROM t ORDER BY CASE WHEN id >= 'pen' THEN 0 ELSE 1 END, id;
> Except that this pretty much kills the query efficiency:
> 0|0|0|SCAN TABLE t
> 0|0|0|USE TEMP B-TREE FOR ORDER BY
> As opposed to the original query whose query plan is:
> 0|0|0|SCAN TABLE t USING COVERING INDEX sqlite_autoindex_t_1
>
> Since fiddling with ORDER BY seems to always kill the index scan
> optimization, I've instead resorted to split the query and use WHERE.
> The following happens to work and to properly make use of indexes:
> SELECT * FROM (SELECT * FROM t WHERE id >= 'pen' ORDER BY id)
> UNION ALL
> SELECT * FROM (SELECT * FROM t WHERE id < 'pen' ORDER BY id);
>
> However, looking at the docs I couldn't find any guarantee that "UNION
> ALL" preserves the inner ordering.
>
>
> Can somebody confirm that the ordering is always preserved when using
> UNION ALL?
> Or can somebody recommend an optimal way to deal with the issue which
> doesn't involve running two separate queries?

Firstly, this is the best method - one I would use. UNION ALL is quite 
efficient.
Secondly, the order by will be  honoured - you can refer to the SQL 
standard for that even, it's an axiom of the output and probably not 
even considered "needed" to mention.

A point that might be worth considering: Union all will not rid 
duplicates and it's probably possible to get results like:

pen
pen
pen
tree

If there are two legitimate copies of pen in your input data and another 
containing a space at the end and you use the RTRIM collation. That may 
or may not be what you want, but should be very easy to deal with either 
way.

Cheers,
Ryan



[sqlite] "Circular" order by

2016-03-09 Thread James K. Lowden
On Wed, 9 Mar 2016 20:43:14 +0200
R Smith  wrote:

> SELECT P.id FROM (
>   SELECT 0 AS sect, id FROM t WHERE id >= 'pen'
>   UNION ALL
> SELECT 1, id FROM t WHERE id < 'pen'
> ) AS P
> ORDER BY P.sect, P.id
> ;

This is the correct answer.  

I'm not sure what you meant by "axiom" in your earlier post.  To the
best of my knowledge, UNION ALL makes no promise about order (and
standard SQL proscribes ORDER BY in a subquery).  If the implementation
finds it more efficient, for instance, to send alternating rows from
each element in the union, it's free to do so.  

--jkl


[sqlite] "Circular" order by

2016-03-09 Thread Alberto Wu
Hi all,

I'm looking for suggestions...
What I want to achieve is to "roll" the result set of a query around by
a certain amount (as in offset + wrap around).

For example, given that:
CREATE TABLE t (id TEXT NOT NULL PRIMARY KEY);
INSERT INTO t VALUES ('pen'), ('tree'), ('desk'), ('car');

I would like to have the same result set of:
SELECT * FROM t ORDER BY id ASC;
car
desk
pen
tree

Except I would like it to start at "pen", producing:
pen
tree
car
desk

Now, one way to do it is:
SELECT * FROM t ORDER BY CASE WHEN id >= 'pen' THEN 0 ELSE 1 END, id;
Except that this pretty much kills the query efficiency:
0|0|0|SCAN TABLE t
0|0|0|USE TEMP B-TREE FOR ORDER BY
As opposed to the original query whose query plan is:
0|0|0|SCAN TABLE t USING COVERING INDEX sqlite_autoindex_t_1

Since fiddling with ORDER BY seems to always kill the index scan
optimization, I've instead resorted to split the query and use WHERE.
The following happens to work and to properly make use of indexes:
SELECT * FROM (SELECT * FROM t WHERE id >= 'pen' ORDER BY id)
UNION ALL
SELECT * FROM (SELECT * FROM t WHERE id < 'pen' ORDER BY id);

However, looking at the docs I couldn't find any guarantee that "UNION
ALL" preserves the inner ordering.


Can somebody confirm that the ordering is always preserved when using
UNION ALL?
Or can somebody recommend an optimal way to deal with the issue which
doesn't involve running two separate queries?

Thanks in advance,
-- Alberto