H,
How to use Pattern Matching function in "group By" clause.
Below is the Data
emp_name transaction_id
John 1001
John= 1002
Peter 1003
I want to group by based on emp_name. Here 'John' and 'John=' both are same employee. I want to ignore if the employee name has '=' sign at the end of the column.
Expected Result:
Emp_name countt
John 2
Peter 1
replace works fine and is fast. But since you asked for pattern matching, here is an answer with a pattern:
SELECT regexp_replace(emp_name, '=$', ''), count(*) AS countt
FROM employees
GROUP BY regexp_replace(emp_name, '=$', '');
A simple case statement replacing only the right most = if one exists.
SELECT case when right(emp_name,1) = '=' then left(emp_Name,len(emp_name-1))
else emp_name end as EmpName, count(Transaction_ID) countt
FROM dataTable
GROUP BY case when right(emp_name,1) = '=' then left(emp_Name,len(emp_name-1))
else emp_name end
select
replace (emp_name, '=', '') as emp_name,
count (*) as countt
from employees
group by replace (emp_name, '=', '')
Edit, since you said the name can contain an =
select
case
when emp_name like '%='
then substr (emp_name, 1, length (emp_name) - 1)
else emp_name
end as emp_name,
count (1) as countt
from employees
group by
case
when emp_name like '%='
then substr (emp_name, 1, length (emp_name) - 1)
else emp_name
end
Related
I have a query below. i can check two conditions. If 'AAA' occurs it will show "AAA" with its id else it will show 'BBB'. I need to check more conditions like
if 'AAA' occurs print "AAA" with its id
else 'BBB' occurs print "BBB" with its id
else 'ccc' occurs print "CCC" with its id
how to do this?
SELECT DISTINCT institution_id,
CASE
WHEN count(*) over(PARTITION BY institution_id) >=5 THEN 'AAA'
WHEN count(*) over(PARTITION BY institution_id) >=5 THEN 'BBB'
END AS status
FROM table
WHERE data LIKE '% AAA%'
WITH
INSTITUTION_COUNT
AS
( SELECT INSTITUTION_ID, COUNT (*) AS INSTITUTION_COUNT
FROM INSTITUTION
GROUP BY INSTITUTION_ID)
SELECT E.INSTITUTION_ID AS INSTITUTION_ID,
DC.INSTITUTION_COUNT AS INSTITUTION_COUNT,
CASE
WHEN DC.INSTITUTION_COUNT < 5 THEN 'Low'
WHEN DC.INSTITUTION_COUNT >= 5 THEN 'High'
END AS STATUS
FROM INSTITUTION E, INSTITUTION_COUNT DC
WHERE E.INSTITUTION_ID = DC.INSTITUTION_ID;
I want to get the records that are having distinct member id using bulk collect.
My query is like below:
...
type members_nt is table of table2%rowtype;
members_bc members_nt;
...
select t2.* bulk collect
into members_bc
from table1 t1
,table2 t2
where t1.isn= t2.isn
and t1.customer= customer
and t1.insert_date between ... and ... );
t2 table has a column named member_id and what I want to get into members_bc is the rows having distinct member_id.
For example if my t2 table has something like this:
name | surname | account | member_id
john alby abc 123
john alby def 123
mary hale lkj 234
I want to bulk collect only
name | surname | account | member_id
john alby abc 123
mary hale lkj 234
or
name | surname | account | member_id
john alby def 123
mary hale lkj 234
It does not matter which one. But memberid must be unique in the members_bc.
How can I achieve this? Thank you.
Use the ROW_NUMBER() analytic function to give each row a number per member_id and then filter to only get the first row.
DECLARE
TYPE members_nt IS TABLE OF table2%ROWTYPE;
members_bc members_nt;
BEGIN
SELECT t2.name, t2.surname, t2.account, t2.member_id
BULK COLLECT INTO members_bc
FROM (
SELECT t2.*,
ROW_NUMBER() OVER ( PARTITION BY member_id ORDER BY ROWNUM ) AS rn
FROM table2 t2
) t2
WHERE rn = 1;
FOR i IN 1 .. members_bc.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( members_bc(i).member_id || ' ' || members_bc(i).name );
END LOOP;
END;
/
outputs:
123 john
234 mary
db<>fiddle here
You can avoid "select *" and just enter the desired columns by creating a cursor for your select and then creating a %rowtype of the cursor.
Using #MT0 answer as baseline template.)
declare
cursor members_cur is
select t2.name, t2.surname, t2.account, t2.member_id
from ( select t2.name, t2.surname, t2.account, t2.member_id
, row_number() over ( partition by member_id order by rownum ) as rn
from table2 t2
) t2
where rn = 1;
type members_tt is table of members_cur%rowtype;
members_bc members_tt;
begin
open members_cur;
fetch members_cur
bulk collect into members_bc;
close members_cur;
for i in 1 .. members_bc.count
loop
dbms_output.put_line(members_bc(i).member_id || ' ' ||members_bc(i).name);
end loop;
end;
A simple option is to use aggregation, e.g.
select name,
surname,
min(account) as account, --> this
member_id
from ...
group by name, surname, member_id --> and this
I have a Oracle procedure inside a package like this
PROCEDURE getEmployee
(
pinLanguage IN VARCHAR2,
pinPage IN NUMBER,
pinPageSize IN NUMBER,
pinSortColumn IN VARCHAR2,
pinSortOrder IN VARCHAR2,
poutEmployeeCursor OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN poutEmployeeCursor FOR
SELECT * FROM (
SELECT EMPLOYEE_ID, USERNAME, FULL_NAME, DATE_OF_BIRTH, EMP.GENDER_ID, GEN_TR.GENDER, EMP.WORK_TYPE_ID, WT_TR.WORK_TYPE, SALARY, EMAIL, PROFILE_IMAGE,
ROW_NUMBER() OVER (ORDER BY EMPLOYEE_ID ASC) RN
FROM EMPLOYEES EMP
INNER JOIN GENDERS GEN ON EMP.GENDER_ID = GEN.GENDER_ID
LEFT JOIN GENDERS_MLD GEN_TR ON GEN.GENDER_ID = GEN_TR.GENDER_ID AND GEN_TR.LANGUAGE = pinLanguage
INNER JOIN WORK_TYPES WT ON EMP.WORK_TYPE_ID = WT.WORK_TYPE_ID
LEFT JOIN WORK_TYPES_MLD WT_TR ON WT.WORK_TYPE_ID = WT_TR.WORK_TYPE_ID AND WT_TR.LANGUAGE = pinLanguage
)
WHERE RN BETWEEN (((pinPage - 1) * pinPageSize) + 1) AND (pinPage * pinPageSize);
END;
I need to make the sort order of the above query dynamic
If I pass the text FullName to pinSortColumn parameter, it need to sort FULL_NAME column
If I pass the text DateOfBirth to pinSortColumn parameter, it need to sort DATE_OF_BIRTH column
If I pass the text Gender to pinSortColumn parameter, it need to sort GEN_TR.GENDER column
I can pass the text asc or desc to pinSortOrder parameter and the query need to be sorted accordingly.
Can you please help me to achieve this?
You can use separate order by for asc and desc as following:
ORDER BY
CASE pinSortOrder WHEN 'asc' THEN
CASE pinSortColumn
WHEN 'FullName' THEN FULL_NAME
WHEN 'DateOfBirth' THEN to_char(DATE_OF_BIRTH,'yyyymmddhh24miss')
WHEN 'Gender' THEN GEN_TR.GENDER
END
END,
CASE pinSortOrder WHEN 'desc' THEN
CASE pinSortColumn
WHEN 'FullName' THEN FULL_NAME
WHEN 'DateOfBirth' THEN to_char(DATE_OF_BIRTH,'yyyymmddhh24miss')
WHEN 'Gender' THEN GEN_TR.GENDER
END
END DESC
Let's say you have passed pinSortColumn as FullName and pinSortOrder as asc then order by clause will be ORDER BY FULL_NAME, NULL DESC (please note that default order will be asc so I have not write it in the code. Query will be ordered by FULL_NAME in ascending manner)
Now, If you have passed pinSortColumn as FullName and pinSortOrder as desc then order by clause will be ORDER BY NULL, FULL_NAME DESC.
Null will not impact ordering.
I hope it is clear now.
Cheers!!
Try this one:
WHERE ...
ORDER BY
CASE pinSortColumn
WHEN 'FullName' THEN FULL_NAME
WHEN 'DateOfBirth' THEN DATE_OF_BIRTH
WHEN 'Gender' THEN GEN_TR.GENDER
END;
However, I don't think you can use ASC, DESC is this way. You to create a dynamic query. For an OPEN ... FOR ... statement it is trivial:
sqlstr := 'SELECT * FROM (
SELECT EMPLOYEE_ID, USERNAME, FULL_NAME, DATE_OF_BIRTH, EMP.GENDER_ID, GEN_TR.GENDER, EMP.WORK_TYPE_ID, WT_TR.WORK_TYPE, SALARY, EMAIL, PROFILE_IMAGE,
ROW_NUMBER() OVER (ORDER BY EMPLOYEE_ID ASC) RN
FROM EMPLOYEES EMP
INNER JOIN GENDERS GEN ON EMP.GENDER_ID = GEN.GENDER_ID
LEFT JOIN GENDERS_MLD GEN_TR ON GEN.GENDER_ID = GEN_TR.GENDER_ID AND GEN_TR.LANGUAGE = :pinLanguage
INNER JOIN WORK_TYPES WT ON EMP.WORK_TYPE_ID = WT.WORK_TYPE_ID
LEFT JOIN WORK_TYPES_MLD WT_TR ON WT.WORK_TYPE_ID = WT_TR.WORK_TYPE_ID AND WT_TR.LANGUAGE = :pinLanguage
)
WHERE RN BETWEEN (((:pinPage - 1) * :pinPageSize) + 1) AND (:pinPage * :pinPageSize)
ORDER BY '
CASE pinSortColumn
WHEN 'FullName' THEN sqlstr := sqlstr || 'FULL_NAME ';
WHEN 'DateOfBirth' THEN sqlstr := sqlstr || 'DATE_OF_BIRTH ';
WHEN 'Gender' THEN sqlstr := sqlstr || 'GEN_TR.GENDER ';
END CASE;
sqlstr := sqlstr || pinSortOrder;
OPEN poutEmployeeCursor FOR sqlstr USING pinLanguage, pinLanguage, pinPage, pinPageSize, pinPage, pinPageSize;
Not in case you run Oracle 12c or newer you can use the Row Limiting Clause instead of dealing with row number.
You can avoid all these duplicated cases. Multiply row number by -1 when descending order is required:
order by
case pinSortOrder when 'desc' then -1 else 1 end *
row_number() over (
order by case pinSortColumn when 'FullName' then full_name end,
case pinSortColumn when 'Gender' then gender end,
case pinSortColumn when 'DateOfBirth' then date_of_birth end)
dbfiddle demo
Also this way you do not have to convert data the same type, no need to use to_char.
I am working on the following query in which
Select name from (Select 'Mana' as name from table1
union
Select 'Tom' as name from dual)A
order by case
when name = 'TOM' then '1'
else name
end ;
which is giving the output
Mana
Tom
and the result i want after the custom sorting is
Tom
Mana
Please don't answer for Order By desc as i have other values in the
column, i need custom sorting
thanks
You're comparing name with 'TOM', but the value you have in that column is 'Tom' - the case doesn't match.
Select name from (Select 'Mana' as name from dual
union
Select 'Tom' as name from dual)A
order by case
when name = 'Tom' then '1'
else name
end ;
NAME
----
Tom
Mana
You might prefer to add a flag to sort by, and then further sort by name:
Select name from (Select 'Mana' as name from dual
union
Select 'Tom' as name from dual)A
order by case when name = 'Tom' then 1 else 2 end,
name;
NAME
----
Tom
Mana
That also avoids any potential issues with character sets - you're assuming that the character '1' always sorted before 'T'. (Which may be true, but still...)
Is it what you need?
order by case
when name = 'TOM' then 1
else 2
end ;
In your order by clause you have a number and varchar as result of case this cause comparing them as varchar pleas use same type.
Select name from
(
Select 'Mana' as name from dual
union all
Select 'Tom' as name from dual
) A
order by
case
when upper(name) = 'TOM' then 1
else 2
end;
And please take care of upper/lower case sensitivity
You can do something like:
order by case
when upper(name) = 'TOM' then 1
else 2
end, name ;
This will keep TOM at top and rest sorted afterwards
I want to hard-code a column name and its value as New York in a select query with pipe delimiter. E.g. Emp table has columns EmpId, EmpName, Salary. I want output such as
Select EmpId ||'|'||
EmpName ||'|'||
'NewYork' as City ||'|'||
Salary
from Emp
Here I want City column in output query and its value should be 'NewYork' for each record.
Here I am getting error as "FROM keyword not found where expected". When I use comma instead of Pipe Delimiter I am getting result but not with Pipe. Please advise. Thanks in advance.
with emps as (
select 1 as id, 'Smith' as name, 2000 as salary from dual
union
select 2, 'Jones', 2200 from dual
)
select
id || '|' || name as record1,
id || '|' || name || '|NewYork|' || salary as record2,
'NewYork' as city
from emps;