T-SQL: How can I order individuals with multiple rows by minimum Rank but keep their rows grouped together? - sql

I'm creating a committee roster report. I have a few hundred rows of data in which I have Name and Position, and each Position has a set Rank, where the lower it is, the higher the Position’s standing in the committee.
I need to sort the VIPs to the top by their most important (lowest) Rank position first and if they have any other positions in the committee, I need those to sort right after that in the list in ascending Rank order, followed by the next best ranking position VIP and their other Positions in ascending Rank order.
Then--after around Rank 100 or so--the list of Positions that are considered VIP ends, so I just want everyone to sort by Name then Rank from then on.
Here's a model of the kind of data I'm trying to sort with it organized in the way I'm hoping for:
RANK
POSITION
NAME
ID
1
Chair
Jane J.
10009
3
Treasurer
Jane J.
10009
9
Editor
Jane J.
10009
2
Vice Chair
Kevin K.
10002
5
Director
Kevin K.
10002
3
President
Laura L.
10003
4
Vice President
Manuel M.
10005
10
Asst. Editor
Manuel M.
10005
100
Member
Anna A.
10010
100
Member
Ben B.
10014
50
Coordinator
Carry C.
10020
100
Member
Carry C.
10020
60
Asst. Coord.
Dennis D.
10008
61
Mbr. Coord.
Dennis D.
10008
Below is a simplified model of the SELECT statement I'm currently working with, which does not accomplish what I'm looking for. I've tried a lot of custom columns in my attempt to make the custom sorting I'm looking for possible, but none have helped.
SELECT
RANK
, POSITION_CODE
, LAST_FIRST
FROM Table
ORDER BY RANK, LAST_FIRST

SELECT
MIN(RANK) OVER (PARTITION BY ID) AS MinRank
, RANK
, POSITION_CODE
, LAST_FIRST
, ID
FROM Table
ORDER BY MinRank, RANK, LAST_FIRST
Edit: Using my new MinRank calculation, I can now first ORDER BY a committee member's Position (POSITION_CODE) with the lowest number RANK (lower numbers for a Position being of greater standing in my committee). This puts the top-ranking Position committee member at the top the top of the list and ascends through everyone else's MinRank, causing the list to sort blocks of names with the same minimum RANK positions together. Then I can ORDER BY RANK ascending, which will sort those blocks in themselves with lesser standing RANK positions. Lastly, I can ORDER BY their names (LAST_FIRST) so that each block of RANK-sorted MinRanks is in alphabetical order by last and first name. For a bonus distinction if needed, I can also ORDER BY ID at the end, to differentiate between those with the same first and last name.
The OVER (PARTITION BY ID) of my MIN(RANK) aggregate is just a shortcut so I don't have to put all my properties into a GROUP BY.

Related

Display all individual rows of a GROUP BY, sorted by the cardinality of the groups

Given a table like this:
User A
---------------
Erik 1278
Bob 16287
Alice 9723
Daniel 7
Erik 8
Bob 162
Erik 126
how to select all lines, grouped/ordered by user with the highest number of rows?
The result would be:
Erik 1278 # Erik is first because 3 rows with him
Erik 8
Erik 126
Bob 16287 # Bob is 2nd because 2 rows
Bob 162
Alice 9723
Daniel 7
Neither
SELECT * FROM t ORDER BY user
nor
SELECT *, COUNT(1) as frequency FROM t GROUP BY user ORDER BY frequency DESC
works, indeed the latter displays only one row for Erik, one row for Bob, one row for ...
It seems like I need a GROUP BY, but still be able to see "each row" of the group. How to do this?
You can use window functions in the order by:
order by count(*) over (partition by user) desc,
user
The first key counts the number of rows per user. The second keeps all users together (which is important if there are ties). You can add a third key if you want for ordering the rows for each user.
EDIT:
In older versions, you can use a subquery:
order by (select count(*) from user u2 where u2.user= u.user) desc,
user

Grouping values and changing values which do not allow the rest of the row to group

Not sure how to describe this, but I want to group a row of values, where one field has two or more different values and set the value of that (but concatenating or changing the values) to give just one single row.
For example:
I have a simple table (all fields are Strings) of people next to their departments. But some people belong to more than one department.
select department_ind, name
from jobs
;
department_ind name
1 Michael
2 Michael
2 Sarah
3 Dave
2 Sally
4 Sally
I want to group by name, and concatenate the department_ind. So the results show look like:
department_ind name
1,2 Michael
2 Sarah
3 Dave
2,4 Sally
Thanks
Use string_agg()
select string_agg(department_ind::text, ',') as departments,
name
from jobs
group by name;

Custom Sort Order in CAML Query

How would one go about telling a CAML query to sort the results in a thoroughly custom order?
.
For instance, for a given field:
-- when equal to 'Chestnut' at the top,
-- then equal to 'Zebra' next,
-- then equaling 'House'?
Finally, within those groupings, sort on a second condition (such as 'Name'), normally ascending.
So this
ID Owns Name
————————————————————
1 Zebra Sue
2 House Jim
3 Chestnut Sid
4 House Ken
5 Zebra Bob
6 Chestnut Lou
becomes
ID Owns Name
————————————————————
6 Chestnut Lou
3 Chestnut Sid
5 Zebra Bob
1 Zebra Sue
2 House Jim
4 House Ken
In SQL, this can be done with Case/When. But in CAML? Not so much!
CAML does not have such a sort operator by my knowledge. The workaround might be that you add a calculated column to the list with a number datatype and formula
=IF(Owns="Chestnut",0,IF(Owns="Zebra",1,IF(Owns="House",3,999))).
Now it is possible to order on the calculated column, which translates the custom sort order to numbers. Another solution is that you create a second list with the items to own, and a second column which contains their sort order. You can link these two lists and order by the item list sort order. The benefit is that a change in the sort order is as easy as editing the respective listitems.

oracle - sql query select max from each base

I'm trying to solve this query where i need to find the the top balance at each base. Balance is in one table and bases are in another table.
This is the existing query i have that returns all the results but i need to find a way to limit it to 1 top result per baseID.
SELECT o.names.name t.accounts.bidd.baseID, MAX(t.accounts.balance)
FROM order o, table(c.accounts) t
WHERE t.accounts.acctype = 'verified'
GROUP BY o.names.name, t.accounts.bidd.baseID;
accounts is a nested table.
this is the output
Name accounts.BIDD.baseID MAX(T.accounts.BALANCE)
--------------- ------------------------- ---------------------------
Jerard 010 1251.21
john 012 3122.2
susan 012 3022.2
fin 012 3022.2
dan 010 1751.21
What i want the result to display is calculate the highest balance for each baseID and only display one record for that baseID.
So the output would look only display john for baseID 012 because he has the highest.
Any pointers in the right direction would be fantastic.
I think the problem is cause of the "Name" column. since you have three names mapped to one base id(12), it is considering all three records as unique ones and grouping them individually and not together.
Try to ignore the "Name" column in select query and in the "Group-by" clause.
SELECT t.accounts.bidd.baseID, MAX(t.accounts.balance)
FROM order o, table(c.accounts) t
WHERE t.accounts.acctype = 'verified'
GROUP BY t.accounts.bidd.baseID;

SELECT datafields with multiple groups and sums

I cant seem to group by multiple data fields and sum a particular grouped column.
I want to group Person to customer and then group customer to price and then sum price. The person with the highest combined sum(price) should be listed in ascending order.
Example:
table customer
-----------
customer | common_id
green 2
blue 2
orange 1
table invoice
----------
person | price | common_id
bob 2330 1
greg 360 2
greg 170 2
SELECT DISTINCT
min(person) As person,min(customer) AS customer, sum(price) as price
FROM invoice a LEFT JOIN customer b ON a.common_id = b.common_id
GROUP BY customer,price
ORDER BY person
The results I desire are:
**BOB:**
Orange, $2230
**GREG:**
green, $360
blue,$170
The colors are the customer, that GREG and Bob handle. Each color has a price.
There are two issues that I can see. One is a bit picky, and one is quite fundamental.
Presentation of data in SQL
SQL returns tabular data sets. It's not able to return sub-sets with headings, looking something a Pivot Table.
The means that this is not possible...
**BOB:**
Orange, $2230
**GREG:**
green, $360
blue, $170
But that this is possible...
Bob, Orange, $2230
Greg, Green, $360
Greg, Blue, $170
Relating data
I can visually see how you relate the data together...
table customer table invoice
-------------- -------------
customer | common_id person | price |common_id
green 2 greg 360 2
blue 2 greg 170 2
orange 1 bob 2330 1
But SQL doesn't have any implied ordering. Things can only be related if an expression can state that they are related. For example, the following is equally possible...
table customer table invoice
-------------- -------------
customer | common_id person | price |common_id
green 2 greg 170 2 \ These two have
blue 2 greg 360 2 / been swapped
orange 1 bob 2330 1
This means that you need rules (and likely additional fields) that explicitly state which customer record matches which invoice record, especially when there are multiples in both with the same common_id.
An example of a rule could be, the lowest price always matches with the first customer alphabetically. But then, what happens if you have three records in customer for common_id = 2, but only two records in invoice for common_id = 2? Or do the number of records always match, and do you enforce that?
Most likely you need an extra piece (or pieces) of information to know which records relate to each other.
you should group by using all your selected fields except sum then maybe the function group_concat (mysql) can help you in concatenating resulting rows of the group clause
Im not sure how you could possibly do this. Greg has 2 colors, AND 2 prices, how do you determine which goes with which?
Greg Blue 170 or Greg Blue 360 ???? or attaching the Green to either price?
I think the colors need to have unique identofiers, seperate from the person unique identofiers.
Just a thought.