Two Level Grouping in SQL - sql

Need Help on this SQL query..
Table has just 3 columns - Name, Check(Yes / No), Post (true /false)
The result gets ordered by Check and then further grouped by Post value....
Simply - All Records with Check Yes and Post true first, then those with Check Yes and Post false. And so on those with Check No and Post true and at last those with Check No and Post false
May be Simpler then it sounds but just can't get this one to work :)
Well the actual table schema is larger then this dummy table but we can assume all these columns to be nvarchars

From my understanding of the question, this seem to be what you want.
SELECT *
FROM dummyTable
ORDER BY [Check] DESC,
[Post] DESC
Will work with BIT and VARCHAR, because T > F AND Y > N.
In case it is VARCHAR with denormalize data, a good idea would be to use
ORDER BY UPPER([Check]) DESC,
UPPER([Post]) DESC
to avoid bad result due to case sensitivity (T < f AND Y < n)

Related

ORDER BY column = value desc|asc

I found this piece of code a while ago, and I don't seem to find any explanation about how it really works:
SELECT account_id from accounts order by account_id = 100;
So, I know what order by [column] desc|asc does to the result set. But I don't seem to find the explanation for giving a value to the [column] and how that affects the result set. It's clearly affected, but I don't seem to find a pattern.
Try rewriting your query using an explicit CASE expression in the ORDER BY clause:
SELECT account_id
FROM accounts
ORDER BY CASE WHEN account_id = 100 THEN 1 ELSE 0 END;
You will observe that all records having account_id != 100 will appear before all records where this is true. When you use:
ORDER BY account_id = 100
Then you are ordering by the boolean equality itself. So, when not true, it would evaluate to zero, and when true would evaluate to one.
Postgres supports boolean types, which take on two values "true" and "false" (plus NULL, of course). A boolean value is being used as the order by key.
The expression account_id = 100 evaluates to "true" for account_id 100 and false for others (or NULL).
What does this do? Well, "true" > "false" and the ordering is ascending. Hence, the true value is ordered after all other values; account_id 100 goes at the end. Well not quite the end. NULL values are lastest -- they would go at the very end.
More commonly, this is done with a descending sort:
order by (account_id = 100) desc
This puts account 100 first in the list.
Note: I put the expression in parentheses in such cases to make it clear that the intent really is to order by the expression. That is, there is no typo.
Basically ORDER BY is of two types ASC and DESC
By Default it is ASC
Let us take an example
SELECT * from Person
ORDER BY Age DESC
Above query returns the Age of Persons in Descending Order

Getting a unique value from an aggregated result set

I've got an aggregated query that checks if I have more than one record matching certain conditions.
SELECT RegardingObjectId, COUNT(*) FROM [CRM_MSCRM].[dbo].[AsyncOperationBase] a
where WorkflowActivationId IN ('55D9A3CF-4BB7-E311-B56B-0050569512FE',
'1BF5B3B9-0CAE-E211-AEB5-0050569512FE',
'EB231B79-84A4-E211-97E9-0050569512FE',
'F0DDF5AE-83A3-E211-97E9-0050569512FE',
'9C34F416-F99A-464E-8309-D3B56686FE58')
and StatusCode = 10
group by RegardingObjectId
having COUNT(*) > 1
That's nice, but then there is one field in AsyncOperationBase that will be unique. Say count(*) = 3, well, AsyncOperationBaseId in AsyncOperationBase will have 3 different values since AsyncOperationBase is the table's primary key.
To be honest, I would not even know what terms and expressions to Google to find a solution.
If anyone has a solution and also, is there any words to describe what I'm looking for ? Perhaps BI people are often faced with such a requirement or something...
I could do it with an SSRS report where the report would visually do the grouping then I could expand each grouped row to get the AsyncOperationBaseId value, but simply through SQL, I can't seem to find a way out...
Thanks.
select * from [CRM_MSCRM].[dbo].[AsyncOperationBase]
where RegardingObjectId in
(
SELECT RegardingObjectId
FROM [CRM_MSCRM].[dbo].[AsyncOperationBase] a
where WorkflowActivationId IN
(
'55D9A3CF-4BB7-E311-B56B-0050569512FE',
'1BF5B3B9-0CAE-E211-AEB5-0050569512FE',
'EB231B79-84A4-E211-97E9-0050569512FE',
'F0DDF5AE-83A3-E211-97E9-0050569512FE',
'9C34F416-F99A-464E-8309-D3B56686FE58'
)
and StatusCode = 10
group by RegardingObjectId
having COUNT(*) > 1
)

Selecting top n Oracle records with ROWNUM still valid in subquery?

I have the following FireBird query:
update hrs h
set h.plan_week_id=
(select first 1 c.plan_week_id from calendar c
where c.calendar_id=h.calendar_id)
where coalesce(h.calendar_id,0) <> 0
(Intention: For records in hrs with a (non-zero) calendar_id
take calendar.plan_week_id and put it in hrs.plan_week_id)
The trick to select the first record in Oracle is to use WHERE ROWNUM=1, and if understand correctly I do not have to use ROWNUM in a separate outer query because I 'only' match ROWNUM=1 - thanks SO for suggesting Questions that may already have your answer ;-)
This would make it
update hrs h
set h.plan_week_id=
(select c.plan_week_id from calendar c
where (c.calendar_id=h.calendar_id) and (rownum=1))
where coalesce(h.calendar_id,0) <> 0
I'm actually using the 'first record' together with the selection of only one field to guarantee that I get one value back which can be put into h.plan_week_id.
Question: Will the above query work under Oracle as intended?
Right now, I do not have a filled Oracle DB at hand to run the query on.
Like Nicholas Krasnov said, you can test it in SQL Fiddle.
But if you ever find yourself about to use where rownum = 1 in a subquery, alarm bells should go off, because in 90% of the cases you are doing something wrong. Very rarely will you need a random value. Only when all selected values are the same, a rownum = 1 is valid.
In this case I expect calendar_id to be a primary key in calendar. Therefor each record in hrs can only have 1 plan_week_id selected per record. So the where rownum = 1 is not required.
And to answer your question: Yes, it will run just fine. Though the brackets around each where clause are also not required and in fact only confusing (me).

How can I get a specific chunk of results?

Is it possible to retrieve a specific range of results? I know how to do TOP x but the result I will retrieve is WAY too big and will time out. I was hoping to be able to pick say the first 10,000 results then the next 10,000 and so on. Is this possible?
WITH Q AS (
SELECT ROW_NUMBER() OVER (ORDER BY ...some column) AS N, ...other columns
FROM ...some table
) SELECT * FROM Q WHERE N BETWEEN 1 AND 10000;
Read more about ROW_NUMBER() here: http://msdn.microsoft.com/en-us/library/ms186734.aspx
Practically all SQL DB implementations have a way of specifying the starting row to return, as well as the number of rows.
For example, in both mysql and postgres it looks like:
SELECT ...
ORDER BY something -- not required, but highly recommended
LIMIT 100 -- only get 100 rows
OFFSET 500; -- start at row 500
Note that normally you would include an ORDER BY to make sure your chunks are consistent
MS SQL Server (being a "pretend" DB) don't support OFFSET directly, but it can be coded using ROW_NUMBER() - see this SO post for more detail.

Using boolean expression in order by clause

I have an order by clause that looks like:
( user_id <> ? ), rating DESC, title
Where ? is replaced with the current user's id.
On postgresql this gives me the ordering I'm looking for i.e. by current user, then highest rating, then title (alphabetically).
However on MySQL I get an unclear order current user is neither first nor last, nor is it by rating or title.
Is my only option for cross database compatibility to replace this quick and dirty boolean expression with a CASE WHEN .. THEN .. ELSE .. END statement?
Edit: Thanks all for the assistance, it is as correctly pointed out by Chaos and Chad Birch the case that the problem lies elsewhere (specifically that I'm using the results of the above query as input into the next - then acting surprised that the order of the first is lost ;)
MySQL has no real notion of booleans, and simply maps TRUE and FALSE to the numeric values 1 and 0 repectively.
In this case user_id <> ? will return 0 for the majority of the rows in your table and 1 for the other rows. The default sort order is ASC, meaning in all likelihood the rows you want are at the bottom of your result set (0/FALSE come before 1/TRUE). Try modifying your query to accommodate this.
( user_id <> ? ) DESC, rating DESC, title
Assuming this is indeed the issue, cross-database compatibility can be achieved with ease.
IF(user = ?, 0, 1), rating DESC, title
You could try doing a
select (user_id <> ?), user_id
to see that you are getting the right true/false values showing up.
I tested several variations on this in mysql and they all worked correctly (the way you're expecting). I suppose your problem has to be somewhere other than the query. To verify for yourself, I suggest running an equivalent query directly from mysql client.