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;