SQL: Sort by priority, but put 0 last - sql

I have a (int) column called "priority". When I select my items I want the highest priority (lowest number) to be first, and the lowest priority (highest number) to be the last.
However, the items without a priority (currently priority 0) should be listed by some other column after the ones with a priority.
In other words. If I have these priorities:
1 2 0 0 5 0 8 9
How do I sort them like this:
1 2 5 8 9 0 0 0
I guess I could use Int.max instead of 0, but 0 makes up such a nice default value which I would try to keep.

I don't think it can get cleaner than this:
ORDER BY priority=0, priority
SQLFiddle Demo
Note that unlike any other solutions, this one will take advantage of index on priority and will be fast if number of records is large.

Try:
order by case priority when 0 then 2 else 1 end, priority

A very simple solution could be to use a composite value/ "prefix" for sorting like this:
SELECT ...
FROM ...
ORDER By CASE WHEN priority = 0 THEN 9999 ELSE 0 END + priority, secondSortCriteriaCol

This will do the trick. You will need to replace testtable with your table name.
SELECT t.priority
FROM dbo.testtable t
ORDER BY (CASE WHEN t.priority = 0 THEN 2147483647 ELSE t.priority END)
In case it's not clear I've picked 2147483647 because this is the max value of the priority column so it will be last.
Mark's answer is better and defo one to go with.

order by case(priority) when 0 then 10 else priority end

Related

Give first duplicate a 1 and the rest 0

I have data which contains 1000+ lines and in this it contains errors people make. I have added a extra column and would like to find all duplicate Rev Names and give the first one a 1 and all remaining duplicates a 0. When there is no duplicate, it should be a 1. The outcome should look like this:
RevName ErrorCount Duplicate
Rev5588 23 1
Rev5588 67 0
Rev5588 7 0
Rev5588 45 0
Rev7895 6 1
Rev9065 4 1
Rev5588 1 1
I have tried CASE WHEN but its not giving the first one a 1, its giving them all zero's.
Thanks guys, I am pulling out my hair here trying to get this done.
You could use a case expression over the row_number window function:
SELECT RevName,
Duplicate,
CASE ROW_NUMER() OVER (PARTITION BY RevName
ORDER BY (SELECT 1)) WHEN 1 THEN 1 ELSE 0 END AS Duplicate
FROM mytable
SQL tables represent unordered sets. There is no "first" of anything, unless a column specifies the ordering.
Your logic suggests lag():
select t.*,
(case when lag(revname) over (order by ??) = revname then 0
else 1
end) as is_duplicate
from t;
The ?? is for the column that specifies the ordering.

MonetDB: Enumerate groups of rows based on a given "boundary" condition

Consider the following table:
id gap groupID
0 0 1
2 3 1
3 7 2
4 1 2
5 5 2
6 7 3
7 3 3
8 8 4
9 2 4
Where groupID is the desired, computed column, such as its value is incremented whenever the gap column is greater than a threshold (in this case 6). The id column defines the sequential order of appearance of the rows (and it's already given).
Can you please help me figure out how to dynamically fill out the appropriate values for groupID?
I have looked in several other entries here in StackOverflow, and I've seen the usage of sum as an aggregate for a window function. I can't use sum because it's not supported in MonetDB window functions (only rank, dense_rank, and row_num). I can't use triggers (to modify the record insertion before it takes place) either because I need to keep the data mentioned above within a stored function in a local temporary table -- and trigger declarations are not supported in MonetDB function definitions.
I have also tried filling out the groupID column value by reading the previous table (id and gap) into another temporary table (id, gap, groupID), with the hope that this would force a row-by-row operation. But this has failed as well because it gives the groupID 0 to all records:
declare threshold int;
set threshold = 6;
insert into newTable( id, gap, groupID )
select A.id, A.gap,
case when A.gap > threshold then
(select case when max(groupID) is null then 0 else max(groupID)+1 end from newTable)
else
(select case when max(groupID) is null then 0 else max(groupID) end from newTable)
end
from A
order by A.id asc;
Any help, tip, or reference is greatly appreciated. It's been a long time already trying to figure this out.
BTW: Cursors are not supported in MonetDB either --
You can assign the group using a correlated subquery. Simply count the number of previous values that exceed 6:
select id, gap,
(select 1 + count(*)
from t as t2
where t2.id <= t.id and t2.gap > 6
) as Groupid
from t;

Updating values with conditions

Let's assume I have a table like that
END
id rank degree
1 4 3
2 3 3
**rank 4 has 4 degrees.
**rank 3 has 4 degrees.
And we will assume that each rank has some number of degrees. I will assume that rank 3 has 4 degrees.
And I want that, when I increase a degree that is the maximum for the current rank, the rank increases by 1 and the degree resets back to 1. For example, I want to increase the degree of the id 2 in the above table by 1. As a result, the rank should be 4 and the degree should be 1.
How can I make that update efficiently in SQL Server 2008?
I assume you want to update all rows with the same rank of the one with id=3.
UPDATE t SET t.rank = t.rank + 1, t.degree = 1
FROM tableName t
WHERE rank = (SELECT rank FROM tableName t2 WHERE id=#id)
Is this what you want?
update t
set degree = (case when degree = 5 then 1 else degree + 1 end),
rank = (case when degree = 5 then rank + 1 else rank end)
where id = 3;
If there is a reference table containing information about the maximum degree for every supported rank (let's call it dbo.ranks), something like this:
rank maxdegree
---- ---------
1 3
2 4
3 5
4 4
... ...
where maxdegree is assumed to be an integer greater than 0, then here's how you could use it:
UPDATE t
SET
t.rank += t.degree / r.maxdegree,
t.degree += t.degree % r.maxdegree + 1
FROM dbo.atable AS t
INNER JOIN dbo.ranks AS r
ON t.rank = r.rank
;
where dbo.atable is assumed to be the name of the table to update.
Note that this query is only intended for increasing by 1. If you want it to be able to increase degree by an arbitrary number, you will need to make more substantial changes than just replacing 1 in the + 1 bit.
Also, this query will not work correctly with some cases of invalid data in your table (like degrees greater than the corresponding maximums), so make sure you've removed any anomalies in your data before trying to use this.

Reorder integer except for value 0 with sql

I'm trying to get an ordered list of rows out of my MYSQL database table based upon an integer value 'place'.
SELECT * FROM mytable
ORDER BY place;
This works okay, except that all rows with value place=0 should appear at the end of the table.
So if my table is:
name place
---- -----
John 1
Do 2
Eric 0
Pete 2
it should become:
name place
---- -----
John 1
Do 2
Pete 2
Eric 0
order by case when place = 0 then 1 else 0 end asc, place asc
that way you get all the non-zeroes ordered first.
SELECT *
FROM myTable
ORDER BY place>0 DESC, place
is a solution without CASE
SELECT *
FROM myTable
ORDER BY CASE place WHEN 0 THEN 9999 ELSE place END
This approach implies that we known that 9999 (or some other value) is bigger than all possible values in the place column.
Alternatively we can sort by two values as in:
ORDER BY CASE place WHEN 0 THEN 0 ELSE -1 END, place

SQL Server cross-row compression

I'm having to return ~70,000 rows of 4 columns of INTs in a specific order and can only use very shallow caching as the data involved is highly volatile and has to be up to date. One property of the data is that it is often highly repetitive when it is in order.
I've started to look at various methods of reducing the row count in order to reduce network bandwidth and client side processing time/resources, but have not managed to find any kind of technique in T-SQL where I can 'compress' repetative rows down into a single row and a 'count' column. e.g.
prop1 prop2 prop3 prop4
--------------------------------
0 0 1 53
0 0 2 55
1 1 1 8
1 1 1 8
1 1 1 8
1 1 1 8
0 0 2 55
0 0 2 55
0 0 1 53
Into:
prop1 prop2 prop3 prop4 count
-----------------------------------------
0 0 1 53 1
0 0 2 55 1
1 1 1 8 4
0 0 2 55 2
0 0 1 53 1
I'd estimate that if this was possible, in many cases what would be a 70,000 row result set would be down to a few thousand at most.
Am I barking up the wrong tree here (is there implicit compression as part of the SQL Server protocol)?
Is there a way to do this (SQL Server 2005)?
Is there a reason I shouldn't do this?
Thanks.
You can use the count function! This will require you to use the group by clause, where you tell count how to break up, or group, itself. Gropu by is used for any aggregate function in SQL.
select
prop1,
prop2,
prop3,
prop4,
count(*) as count
from
tbl
group by
prop1,
prop2,
prop3,
prop4,
y,
x
order by y, x
Update: The OP mentioned these are ordered by y and x, not part of the result set. In this case, you can still use y and x as part of the group by.
Keep in mind that order means nothing if it doesn't have ordering columns, so in this case, we have to respect that with y and x in the group by.
This will work, though it is painful to look at:
;WITH Ordering
AS
(
SELECT Prop1,
Prop2,
Prop3,
Prop4,
ROW_NUMBER() OVER (ORDER BY Y, X) RN
FROM Props
)
SELECT
CurrentRow.Prop1,
CurrentRow.Prop2,
CurrentRow.Prop3,
CurrentRow.Prop4,
CurrentRow.RN -
ISNULL((SELECT TOP 1 RN FROM Ordering O3 WHERE RN < CurrentRow.RN AND (CurrentRow.Prop1 <> O3.Prop1 OR CurrentRow.Prop2 <> O3.Prop2 OR CurrentRow.Prop3 <> O3.Prop3 OR CurrentRow.Prop4 <> O3.Prop4) ORDER BY RN DESC), 0) Repetitions
FROM Ordering CurrentRow
LEFT JOIN Ordering O2 ON CurrentRow.RN + 1 = O2.RN
WHERE O2.RN IS NULL OR (CurrentRow.Prop1 <> O2.Prop1 OR CurrentRow.Prop2 <> O2.Prop2 OR CurrentRow.Prop3 <> O2.Prop3 OR CurrentRow.Prop4 <> O2.Prop4)
ORDER BY CurrentRow.RN
The gist is the following:
Enumerate each row using ROW_NUMBER OVER to get the correct order.
Find the maximums per cycle by joining only when the next row has different fields or when the next row does not exist.
Figure out the count of repetitions is by taking the current row number (presumed to be the max for this cycle) and subtracting from it the maximum row number of the previous cycle, if it exists.
70,000 rows of four integer columns is not really a worry for bandwidth on a modern LAN, unless you have many workstations executing this query concurrently; and on a WAN with more restricted bandwidth you could use DISTINCT to eliminate duplicate rows, an approach which would be frugal with your bandwidth but consume some server CPU. Again, however, unless you have a really overloaded server that is always performing at or near peak loads, this additional consumption would be a mere blip. 70,000 rows is next to nothing.