SQL order by Issue-Continued - sql
This issue is a continuation of my earlier query. It's still not working.
It's about an ORDER BY clause. I am trying to sort using a variable called "sortby".
Here, now the ORDER BY clause is selected as a separate column using the DECODE() function (as suggested in the answer by #devio in the original version of this question).
Let’s say sortby = ‘memberCount’ in this case, I passed it as first argument in decode(); memberCount is a COLUMN in the grptest table.
select distinct gl.group_id,
decode('memberCount', 'name', gl.group_name_key,
'description', gl.group_description_key,
'memberCount', gl.member_count)
as p_sortby,
gl.group_name,
gl.group_description,
gl.status_code,
gl.member_count,
(select grpp.group_name
from grptest_relationship grel join grptest grpp
on grel.parent_group_id = grpp.group_id
where grel.child_group_id = gl.group_id) as parent_group_name,
gl.group_name_key,
gl.group_description_key
from grptest gl
where gl.group_org_id = '3909'
and (gl.group_name_key like '%' || 'GROUP' || '%')
order by 2;
It doesn’t work.
But if I pass ‘name’ as first argument in decode above, it works.
That’s my original issue about why it doesn’t apply on memberCount.
I commented:
What is the error you get? Or what is the erroneous behaviour you get? In my adaptation of your question to my database, I had to ensure that the numeric column was converted to a character type before the DECODE() was acceptable - the other two columns were character columns. With that done, and with the minor issue that sorting numbers alphabetically places '8' after '79' and before '80', I got an appropriate result.
Rohit asked:
Thanks for the inputs. I guess I am confused about the minor issue you mentioned that "that sorting numbers alphabetically places '8' after '79' and before '80'". I couldn't get what is the thing here? Also, could you please help in my query of how "to ensure that the numeric column was converted to a character type before the DECODE() was acceptable". Can you please modify my query above in this respect?
The table I used is for the 'table of elements':
-- Tables for storing information about chemical elements and chemical compounds
-- See: http://www.webelements.com/ for elements.
-- See: http://ie.lbl.gov/education/isotopes.htm for isotopes.
CREATE TABLE elements
(
atomic_number INTEGER NOT NULL UNIQUE
CHECK (atomic_number > 0 AND atomic_number < 120),
symbol CHAR(3) NOT NULL UNIQUE,
name CHAR(20) NOT NULL UNIQUE,
atomic_weight DECIMAL(8,4) NOT NULL,
stable CHAR(1) DEFAULT 'Y' NOT NULL
CHECK (stable IN ('Y', 'N'))
);
It's an interesting table because it has three genuine candidate keys (atomic number, name and symbol are each unique), and depending on context (isotopes vs chemicals), you are better off using atomic number or symbol as the joining key.
The queries I used were:
select decode('atomic_number',
'name', name,
'symbol', symbol,
'atomic_number', atomic_number||''),
name, symbol, atomic_number
from elements
order by 1;
select decode('name',
'name', name,
'symbol', symbol,
'atomic_number', atomic_number||''),
name, symbol, atomic_number
from elements
order by 1;
select decode('symbol',
'name', name,
'symbol', symbol,
'atomic_number', atomic_number||''),
name, symbol, atomic_number
from elements
order by 1;
These demonstrated the three orderings - by symbol, by name, and by atomic number.
Part of the result set for the atomic number ordering was:
77 Iridium Ir 77
78 Platinum Pt 78
79 Gold Au 79
8 Oxygen O 8
80 Mercury Hg 80
81 Thallium Tl 81
Because the atomic number was coerced into a string, the sort was in string order, and when regarded as a string, '8' appears after '79' and before '80', as shown. One way of avoiding that problem would be:
select decode('atomic_number',
'name', name,
'symbol', symbol,
'atomic_number', lpad(atomic_number, 3)),
name, symbol, atomic_number
from elements
order by 1;
Producing the following (which, though it isn't obvious, has an extra blank at the start of the first column):
77 Iridium Ir 77
78 Platinum Pt 78
79 Gold Au 79
80 Mercury Hg 80
81 Thallium Tl 81
82 Lead Pb 82
This uses the knowledge that space precedes any digit in the (ASCII, Latin-1, Unicode) sort sequence, and that atomic numbers are not more than 3 digits. Alternatively, I could have used 'LPAD(atomic_number, 3, '0')' to zero-pad the data. I tested with IBM Informix Dynamic Server (IDS) 11.50.FC3W2 on Solaris 10. IDS is very tolerant of type mismatches and automatically converts the atomic_number argument to LPAD into a string. Other DBMS may not be so tolerant; you'd have to explicitly cast the value.
Going back to the question...
Assuming memberCount is a numeric column and the values are not more than 4 digits long (adjust appropriately if they are longer), the query can be written:
select distinct gl.group_id,
decode('memberCount', 'name', gl.group_name_key,
'description', gl.group_description_key,
'memberCount', LPAD(gl.member_count, 4))
as p_sortby,
gl.group_name,
gl.group_description,
gl.status_code,
gl.member_count,
(select grpp.group_name
from grptest_relationship grel join grptest grpp
on grel.parent_group_id = grpp.group_id
where grel.child_group_id = gl.group_id) as parent_group_name,
gl.group_name_key,
gl.group_description_key
from grptest gl
where gl.group_org_id = '3909'
and (gl.group_name_key like '%' || 'GROUP' || '%')
order by 2;
Or you might need:
LPAD(CAST(memberCount AS CHAR(4)), 4)
or some other slightly DBMS-specific incantation that achieves the same general effect.
Since you didn't provide a schema (much less sample data) for the query, I don't have your table in my database, so I can't demo your query working
I know that
order by p_sortby
wont work, but could you try
order by decode('memberCount',
'name', gl.group_name_key,
'description', gl.group_description_key,
'memberCount', gl.member_count)
EDIT:
I remembered another way:
select * from (
select column1, decode(....) as column2, .... from table1
) t1
order by 2
and this way might even be a faster one
Related
How can I group rows in SQL and sum them up?
Suppose I have a table like this in SQL Server 2017, let's call it "maps_and_cups" some_code quantity big_map 6 tiny_map 5 big_cup 10 tiny_cup 4 I would like to know the best way to group the maps and cups into one, in this way. some_code quantity maps 11 cups 14 I know that it is using "if" and "case", adding and comparing if it is a tiny_map, a big_map, and so on, I have seen several examples but I cannot make it compile.
You can indeed use a case when expression. For instance: with base as (select case some_code when 'big_map' then 'maps' when 'tiny_map' then 'maps' when 'big_cup' then 'cups' when 'tiny_cup' then 'cups' else 'other' end grp, quantity from maps_and_cups) select grp, sum(quantity) quantity from base group by grp; However, if you're going to list each and every code explicitly, you might as well create a reference table for it: some_code grp big_map maps tiny_map maps big_cup cups tiny_cup cups ...and then join that table into your query: select grp, sum(quantity) from maps_and_cups a left join ref_maps_cups b on a.some_code = b.some_code group by grp;
You can solve this task using "case" and "charindex" functions, like this: declare #t table (some_code varchar (20), quantity int) insert into #t values ('big_map', 6), ('tiny_map', 5), ('big_cup',10), ('tiny_cup', 4) select case when charindex ('map', some_code)>0 then 'map' when charindex ('cup', some_code)>0 then 'cup' end some_code ,sum(quantity) quantity from #t group by case when charindex ('map', some_code)>0 then 'map' when charindex ('cup', some_code)>0 then 'cup' end OUTPUT:
If you just want the right three characters for aggregating, you can use right(): select right(some_code, 3) + 's', sum(quantity) from maps_and_cups group by right(some_code, 3) + 's';
You are creating a problem for yourself as you're (probably) breaking the first normal form by storing non atomic values in the field "some_code". (Some field name i'd say. ;) Why not separating the value into [ type ] and [ size ] ?
Assign custom labels to sql query
This is a pretty simple question, I think, but I cannot find an answer for it here, or through online searching. I am running a count query, and am having trouble with value labels and column headings Query is SELECT mail_code AS "code", COUNT(mail_code) AS "count" FROM data.base GROUP BY mail_code ; I get back: C Count - ----- Y 110 X 785 Z 92 Questions: How do I get the first variable (code) to display its full name, instead of a single letter? How do I change Y, X, and Z to read "phone," "mail," and "email" ...or anything else for that matter? The length of the mail_code variable is 1 byte...is that why only the first letter is showing up as my varname? ...I was initially warned that based on my title, it might get downvoted. OK, but I tried to look elsewhere for the answer and could not find it, IE I tried due diligence. Thank you in advance.
Mostly, they keep a look-up table with foreign - primary key relationship. i.e. write explanation in a table with code_name and explanation columns with values X , phone ; Y, mail : Z , email respectively, and Join them with a SQL statement : select d.mail_code as "code", c.explanation as "communication type", count(1) as "count" from data_base d inner join codes c on ( d.mail_code = c.mail_code ) group by d.mail_code, c.explanation; Where DDLs are as following to create tables : create table codes(mail_code varchar2(1) primary key,explanation varchar2(15)); create table data_base( mail_code varchar2(1) references codes(mail_code)); Demo
Thank you all, I got about 75% of the way there. Syntax: COLUMN mail_code FORMAT A10 SELECT DECODE (mail_code, 'X', 'mail', 'Y', 'phon', 'Z', 'emai') AS "code", COUNT(*) AS "count" FROM data.base GROUP BY mail_code ; Returned code count ---- ----- mail 110 phon 785 emai 92 Double quotes are needed b/c Oracle. It'll work to change name to 'code' wo double quotes but comes up as all caps (CODE) wo them, and I can choose w double quotes. Thank you all for your help with this!
Oracle Query on Comma Separated Values In a Column
I have two tables. One table is a list of access points. The other is a list of who has access to what. It use to be a person had access to either one thing or all things. But, now that has changed. Now someone might have access to several points. The wrench in the system is that in the column that shows what they have access to may have a single value, "ALL" for all access or a comma separated list (which is new). I originally thought I could just do WHERE Access_To Here IN(), but I am unsure how to convert the value of Access_To to a formatted list. I need to be able to do this as a single query since I am using it for a LOV in APEX. So, I need some help. The Access_Points_Table has only one column, Access_Points. Here are some example values that it might have: CSX CZR XR3 NBO QHG The Users_List table has several columns, but the most important are User_Name, Access_To. Here are some example values that it might have: Joe | ALL Fred | CSX Allen | CZR, NBO Hank | QHG Here is query I am currently using, but it only works if there is only a single value in the Access_To column. SELECT DISTINCT Access_Points VALUE FROM Access_Points_Table apt JOIN Users_List ul ON (ul.wwid = 'ZZ999' AND (ul.Access_To = 'ALL' OR apt.symbol_name = ul.Access_To)) ORDER BY name ASC What I am trying to accomplish is: SELECT DISTINCT Access_Points VALUE FROM Access_Points_Table apt JOIN Users_List ul ON (ul.wwid = 'ZZ999' AND (ul.Access_To = 'ALL' OR apt.symbol_name IN(Something Goes Here))) ORDER BY name ASC So, if run a query for the user Allen, it will return the rows: CZR NBO
Depending on the length of your comma-delimited data, you can use Oracle's regular expression engine (the difficulty is that Oracle limits regular expressions to 512 bytes): SELECT DISTINCT Access_Points VALUE FROM Access_Points_Table apt JOIN Users_List ul ON ( ul.wwid = 'ZZ999' AND ( ul.Access_To = 'ALL' OR REGEXP_LIKE(apt.symbol_name, '^(' || REPLACE(ul.Access_To, ',', '|') || ')$') ) ) ORDER BY name ASC Alternately you can use LIKE: SELECT DISTINCT Access_Points VALUE FROM Access_Points_Table apt JOIN Users_List ul ON ( ul.wwid = 'ZZ999' AND ( ul.Access_To = 'ALL' OR ',' || ul.Access_To || ',' LIKE '%,' || apt.symbol_name || ',%' ) ) ORDER BY name ASC Note that if Access_To has spaces after its commas as in your OP, it does add some complexity but that can be overcome, simply REPLACE(ul.Access_To, ' '). By the way, I do wonder why this: ul.wwid = 'ZZ999' is in the ON clause instead of in a WHERE clause.
Another way would be to use the instr test: SELECT DISTINCT access_points VALUE FROM access_points_table a JOIN users_list u ON ( INSTR(u.access_to,a.access_points,1) > 0 ) ORDER BY 1
Filter 2-dimensional array
I have this array: 1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0,11:0,12:0,13:0,14:0,15:0,16:0 17:0,18:0,19:0,20:0,21:0,22:0,23:0,24:0,25:0,26:0,27:0,28:0,29:0,30:0,31:0,32:0, 49:0,33:0,34:0,35:0,36:0,37:0,38:0,39:0,40:0,41:0,42:0,43:0,44:0,45:0,46:0,47:0, 48:0,50:0,51:0,52:0,53:0,54:0,55:0,56:0,57:0,58:0,59:0,60:0,61:0,62:0,63:9,64:0, 65:0,66:0,67:0,68:0,69:0,70:0,71:0,72:0,73:0,74:0,75:0,76:0,77:0,78:0,79:0,80:0, 81:0,82:0,83:0,84:0,85:0,86:0,87:0,88:0,89:0,90:0,91:0,92:0,93:0,94:0,95:0,96:0, 97:0,98:0,99:0,100:0 I want to filter all entry like *:0 so that I only get this result: 63:9 I think I have to describe it better: I have a table users with a field user_skill. In this field is a such a string: 1:0, 2:0, 3:0, 4:3, 5:8, 6:9, 7:0, 8:0, 9:0 with this syntax: skill_id:prio,skill_id:prio,skill_id:prio,skill_id:prio,... Now I want to to join the users table with the skills table like this: SELECT skill_name FROM users inner join skills on skills.skill_id = ANY (string_to_array(regexp_replace(user_skill,':[0-9]*','','g'),',')::int[]) where user_id = 16 order by skill_name That works well but I only want to see skill_name where the user has prio <> 0.
Proper solution You might want to familiarize yourself with normalization and implement this as a proper n:m relation between the tables users and skills with an additional attribute prio in the user_skill table. Here is a complete recipe: How to implement a many-to-many relationship in PostgreSQL? Then your query can be very simple: SELECT s.skill_name FROM user_skill uk JOIN skills s USING (skill_id) WHERE uk.user_id = 16 AND uk.prio <> 0 ORDER BY s.skill_name; It can (and should) be backed up with indices and will be faster by several orders of magnitude than what you have right now. It will need some more space on disk. Solution for the dark side While being locked in this unfortunate situation you can help yourself with this query. However, this assumes at least Postgres version SELECT s.skill_name FROM ( SELECT split_part(us_item, ':', 1) AS skill_id FROM ( SELECT trim(unnest(string_to_array(user_skill, ','))) AS us_item FROM users WHERE user_id = 16 -- enter user_id here ) x WHERE split_part(us_item, ':', 2) <> '0' ) u JOIN skills s USING (skill_id) ORDER BY 1; Demo with example: SELECT split_part(us_item, ':', 1) AS skill_id FROM ( SELECT trim(unnest(string_to_array( '1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0,11:0,12:0,13:0,14:0,15:0,16:0,' '17:0,18:0,19:0,20:0,21:0,22:0,23:0,24:0,25:0,26:0,27:0,28:0,29:0,30:0,31:0,32:0,' '49:0,33:0,34:0,35:0,36:0,37:0,38:0,39:0,40:0,41:0,42:0,43:0,44:0,45:0,46:0,47:0,' '48:0,50:0,51:0,52:0,53:0,54:0,55:0,56:0,57:0,58:0,59:0,60:0,61:0,62:0,63:9,64:0,' '65:0,66:0,67:0,68:0,69:0,70:0,71:0,72:0,73:0,74:0,75:0,76:0,77:0,78:0,79:0,80:0,' '81:0,82:0,83:0,84:0,85:0,86:0,87:0,88:0,89:0,90:0,91:0,92:0,93:0,94:0,95:0,96:0,' '97:0,98:0,99:0,100:0', ','))) AS item ) x WHERE split_part(us_item, ':', 2) <> '0'; trim() deals with leading and trailing spaces, like you have in your example. But those may just be artifacts in the sloppy question. I fixed a missing ,. BTW, the SQL standard allows to enter string literal like I demonstrate. Weird, but sometimes useful.
SQL Sorting using Order by
Can you all please help me with this? Presently, I have this SELECT which returns data ordered by this way SELECT DISTINCT gl.group_id, gl.group_name, gl.group_description, gl.status_code, gl.member_count, ( SELECT grpp.group_name FROM test_group_relationship grel JOIN test_group grpp ON grel.parent_group_id = grpp.group_id WHERE grel.child_group_id = gl.group_id ) AS parent_group_name, gl.group_name_key, gl.group_description_key FROM test_group gl WHERE gl.group_org_id = '3909' AND gl.group_name_key like '%' || 'GROUP' || '%' ORDER BY gl.group_name_key, CONVERT(gl.group_name, 'WE8EBCDIC500') The output is below.I have tried indenting the columns to paste the data. GROUP_NAME GROUP_NAME_KEY Add Group Basic Flow ADD GROUP BASIC FLOW Administrative Group ADMINISTRATIVE GROUP Amy Group 33 AMY GROUP 33 Amy Test Group 1 AMY TEST GROUP 1 another add group test from matt ANOTHER ADD GROUP TEST FROM MATT **My Question is in the FIELD GROUP_NAME--> how can i SORT DATA using ORDER BY so that lowercase letters will be sorted before uppercase letters. Expected output is :- the value "another add group test from matt" has to come at the first place.This way lowercase letters are sorted first and then UPPER CASE. See also: SQL ORDER BY Issue Continued PLSQL ORDER BY Issue
Convert the type on the field to a collation that is case sensitive and order by it asc In your order by add Group_Name COLLATE Latin1_General_CS_AS Asc assuming that your characters are in english; otherwise substitute french etc.
try: ORDER BY UPPER (SUBSTR (GROUP_NAME, 1, 1)), SUBSTR (GROUP_NAME, 1, 1) DESC, UPPER(GROUP_NAME), GROUP_NAME DESC;
Just add BINARY to your ORDER BY. ORDER BY BINARY gl.group_name_key You may have to use DESC otherwise upper case will come first. But then that would also sort z-a.
I'm not an Oracle guy, but depending on your version of Oracle, I believe that there are some session variables that will determine this for you. You can try the following: ALTER SESSION SET nls_comp=binary; ALTER SESSION SET nls_sort=GENERIC_M_CI;