I have a table with the below sample records
Name Profession University
----- -------------- ---------------
Ram Student xxxUniversity
Ravi Professor xyzUniversity
Reshma Professor abcUniversity
Ram StudenT xxxUniversity
As seen above "Ram" has case sensitive duplicate records. My Oracle query should return distinct records and one record for "Ram", which has the maximum number of uppercase character in Profession column.
Expected result
Name Profession University
----- -------------- ---------------
Ravi Professor xyzUniversity
Reshma Professor abcUniversity
Ram StudenT xxxUniversity
Try this :
SELECT name,
Profession,
university
FROM
(SELECT name,
Profession,
university,
ROW_NUMBER() OVER (PARTITION BY NAME,UPPER(PROFESSION) ORDER BY REGEXP_COUNT (Profession, '[A-Z]') DESC) RN
FROM table1)
WHERE RN=1;
Get the row number partitioned over the unique values for each column converted to upper (or lower) case:
SELECT name, profession, university
FROM (
SELECT name, profession, university,
ROW_NUMBER()
OVER (
PARTITION BY
UPPER( name ),
UPPER( profession ),
UPPER( university )
ORDER BY
LENGTH( TRANSLATE( profession, ' abcdefghijklmnopqrstuvwxyz', ' ' ) ) DESC
)
AS rn
FROM your_table
)
WHERE rn = 1;
Related
I have a requirement to find emplid having data difference in same table. Table consist of 50-60 columns.. I need to check if any column has change in data from previous row, emplidshould get pick up as well as if any new employee get add that also needs to pick up..
I have created a basic query and it is working but need some way to achieve same purpose as I do not want to write every column name.
My query:
select
emplid
from
ps_custom_tbl t, ps_custom_tbl prev_t
where
prev_t.emplid = t.emplid
and t.effdt = (select max effdt from ps_custom_tbl t2
where t2.emplid = t.emplid)
and prev_t.effdt = (select max(effdt) from ps_custom_tbl prev_t2
where emplid = prev_t.emplid and effdt < t.effdt)
and (t.first_name prev_t.first_name Or t.last_name prev_t.last_name …. 50 columns);
Can you please suggest another way to achieve same thing?
You can use MINUS.
if no_data then both are the same, if there are some records - mean that there is a difference between
create table emp as select * from hr.employees;
insert into emp select employee_id+1000, first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id,
decode(department_id ,30,70, department_id)
from hr.employees;
select first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id
from emp where employee_id <= 1000
minus
select first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id
from emp where employee_id > 1000;
But you have to list all columns, because if you have eg different dates or ids - they will be compared too. But it's easier to list columns in SELECT clause then write for everyone WHERE condition.
Maybe it will help.
-- or if different tables and want to compare all cols simply do
drop table emp;
create table emp as select * from hr.employees;
create table emp2 as
select employee_id, first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id,
decode(department_id ,30,70, department_id) department_id
from hr.employees;
select * from emp
minus
select * from emp2;
---- ADD DATE CRITERIA
-- yes, you can add date criteria and using analytical functions check which
-- is newer and which is
older and then compare one to another. like below:
drop table emp;
create table emp as select * from hr.employees;
insert into emp
select
employee_id,
first_name,
last_name,
email,
phone_number,
hire_date+1,
job_id,
salary,
commission_pct,
manager_id,
decode(department_id ,30,70, department_id)
from hr.employees;
with data as --- thanks to WITH you retrieve data only once
(select employee_id, first_name, last_name, email, phone_number,
hire_date,
row_number() over(partition by employee_id order by hire_date desc) rn -- distinguish newer and older record,
job_id, salary, commission_pct, manager_id, department_id
from emp)
select employee_id, first_name, last_name, email, phone_number, department_id from data where rn = 1
MIUNUS--- find the differences
select employee_id, first_name, last_name, email, phone_number, department_id from data where rn = 2;
You will have to write all columns in some sense no matter what you do.
In terms of comparing current and previous, you might find this easier
select
col1,
col2,
...
lag(col1) over ( partition by empid order by effdt ) as prev_col1,
lag(col2) over ( partition by empid order by effdt ) as prev_col2
...
and then you comparison will be along the lines of
select *
from ( <query above >
where
decode(col1,prev_col1,0,1) = 1 or
decode(col2,prev_col2,0,1) = 1 or
...
The use of DECODE in this way handles the issues of nulls.
My requirement is to send out data to managers, they change any/all/none of the data in the columns, and send back to me. I then have to identify each column that has a difference from what I sent, and mark those columns as changed for a central office reviewer to visually scan and approve/deny the changes for integration back into the central data set.
This solution may not fit your needs of course, but a template structure is offered here that you can augment to meet your needs no matter the number of columns. In the case of your question, 50-60 columns will make this SQL query huge, but I've written heinously long queries in the past with great success. Add columns a few at a time rather than all wholesale according to this template and see if they work along the way.
You could easily write pl/sql to write this query for you for the tables in question.
This would get very cumbersome if you had to compare columns from 3 or more tables or bi-directional changes. I only care about single direction changes. Did the person change my original row columns or not. If so, what columns did they change, and what was my before value and what is their after value, and show me nothing else please.
In other words, only show me rows with columns that have changes with their before values and nothing else.
create table thing1 (id number, firstname varchar2(10), lastname varchar2(10));
create table thing2 (id number, firstname varchar2(10), lastname varchar2(10));
insert into thing1 values (1,'Buddy', 'Slacker');
insert into thing2 values (1,'Buddy', 'Slacker');
insert into thing1 values (2,'Mary', 'Slacker');
insert into thing2 values (2,'Mary', 'Slacke');
insert into thing1 values (3,'Timmy', 'Slacker');
insert into thing2 values (3,'Timm', 'Slacker');
insert into thing1 values (4,'Missy', 'Slacker');
insert into thing2 values (4,'Missy', 'Slacker');
commit;
Un-comment commented select * queries one at a time after each data set to understand what is in each data set at each stage of the refinement process.
with rowdifferences as
(
select
id
,firstname
,lastname
from thing2
minus
select
id
,firstname
,lastname
from thing1
)
--select * from rowdifferences
,thing1matches as
(
select
t1.id
,t1.firstname
,t1.lastname
from thing1 t1
join rowdifferences rd on t1.id = rd.id
)
--select * from thing1matches
, col1differences as
(
select
id
,firstname
from rowdifferences
minus
select
id
,firstname
from thing1matches
)
--select * from col1differences
, col2differences as
(
select
id
,lastname
from rowdifferences
minus
select
id
,lastname
from thing1matches
)
--select * from col2differences
,truedifferences as
(
select
case when c1.id is not null then c1.id
when c2.id is not null then c2.id
end id
,c1.firstname
,c2.lastname
from col1differences c1
full join col2differences c2 on c1.id = c2.id
)
--select * from truedifferences
select
t1m.id
,case when td.firstname is not null then t1m.firstname end beforefirstname
,td.firstname afterfirstname
,case when td.lastname is not null then t1m.lastname end beforelastname
,td.lastname afterlastname
from thing1matches t1m
join truedifferences td on t1m.id = td.id
;
I have a table called employee and has columns as follows
emp_id number
emp_name varchar(30)
salary float
dept_id number
I want to get the output as any one name of employee within that department and employee count from each department. I tried the below, but didn't work well
SELECT emp_name, count(*) FROM emp
GROUP BY dept_id, emp_name;
Expected output:
emp_name, count(*)
abc, 4
def, 2
xyz, 10
Can anyone suggest?
You can try this if you want just a basic "random employee" shown for each department.
select emp_name, emp_count
from (
select emp_name, dept_id,
count(*) over (partition by dept_id) emp_count,
row_number() over (partition by dept_id
order by dbms_random.value ) rnum
from employee
)
where rnum = 1
/
This uses analytic function to calculate the counts, and then pick off 1 random row to display.
I have a record of an employee but my query is returning 2 records of this employee because the address column is different between the 2. How can solve this problem? Is it something that can be done? EMP_ID, CUS_LAST_NAME, CUS_FIRST_NAME, and GUARDIAN_ADDRESS are from 3 separate tables.
Example:
ID EMP_ID CUS_LAST_NAME CUS_FIRST_NAME GUARDIAN_ADDRESS
00000000 11111111 Jackson Michael 1111 Street Apt 1
ID EMP_ID CUS_LAST_NAME CUS_FIRST_NAME GUARDIAN_ADDRESS
00000000 11111111 Jackson Michael 1111 Street
if you can't the delete one of the two
if you don't matter which address the select return you can use an aggregation function for get one row only
select ID , EMP_ID , EMP_LAST_NAME, EMP_FIRST_NAME, min(ADDRESS)
from my_table
group by ID , EMP_ID , EMP_LAST_NAME, EMP_FIRST_NAME
If you want detect what employee have duplicates entries.
SELECT *
FROM employees
WHERE EMP_ID IN (
SELECT EMP_ID
FROM employees
GROUP BY EMP_ID
HAVING COUNT(*) > 1
)
--start with unique list of clients
SELECT DISTINCT a.ID, a.EMP_ID, e.EMP_LAST_NAME, e.EMP_FIRST_NAME, e.ADDRESS
FROM TABLE1 a
--attach on employee data on id
OUTER APPLY (SELECT TOP 1 b.EMP_LAST_NAME, b.EMP_FIRST_NAME, b.ADDRESS
FROM TABLE2 b
WHERE a.id = b.id
--use order by clause to change order and choose what top employee record u want to choose
ORDER BY b.address
) e
The quick and dirty way with max():
select id, emp_id, emp_last_name, emp_first_name, max(address) as address
from employees
group by id, emp_id, emp_last_name, emp_first_name
Alternative using: top with ties
select top 1 with ties
id, emp_id, emp_last_name, emp_first_name, address
from employees
order by row_number() over (partition by emp_id order by address desc)
rextester demo for both: http://rextester.com/EGGA75008
for Oracle 12c... I have a table of line items sold to a company. This table has a 3-tier level hierarchy of the rep who sold to this company. One of the columns is the company name. I need help writing the SQL to generate a comma separated, unique list of the names of ALL the people, across all three columns, across all rows sold to this company. For an example...
CompanyName Rep Manager GVP
----------- ------- -------- --------
Sears Bob Tim Frank
Sears Jack Tim Frank
Ace Scott Chris Bill
When I look at Sears, the SQL should return 'Bob, Jack, Tim, Frank'. The ORDER of the names does NOT matter, only that they are unique, and that they include names from all 3 fields. I would assume that this is a type of ListAgg query, but could be wrong...
Use the UNPIVOT operator (it will only do a single table scan whereas using UNION will typically do one table scan for each SELECT in the unioned statement):
Oracle Setup:
CREATE TABLE table_name ( CompanyName, Rep, Manager, GVP ) AS
SELECT 'Sears', 'Bob', 'Tim', 'Frank' FROM DUAL UNION ALL
SELECT 'Sears', 'Jack', 'Tim', 'Frank' FROM DUAL UNION ALL
SELECT 'Ace', 'Scott', 'Chris', 'Bill' FROM DUAL;
Query:
SELECT CompanyName,
LISTAGG( Name, ',' ) WITHIN GROUP ( ORDER BY Name ) AS Names
FROM (
SELECT DISTINCT
CompanyName,
Name
FROM table_name
UNPIVOT( name FOR typ IN ( Rep, Manager, GVP ) )
)
GROUP BY CompanyName;
Output:
COMPANYNAME NAMES
----------- ------------------
Ace Bill,Chris,Scott
Sears Bob,Frank,Jack,Tim
You need to unpivot the data (to remove duplicates) and then reaggregate it:
select companyname, listagg(person, ',') within group (order by person) as persons
from ((select companyname, repfrom as person t) union
(select companyname, manager from t) union
(select companyname, gvp from t)
) t
group by companyname;
This SQL should do the trick:
select listagg(p, ', ') within group (order by p) from (
select rep p
from your_table
union
select manager p
from your_table
union
select gvp p
from your_table);
I need to combine the two MySQL statements below into a single ORACLE query if possible.
The initial query is
SELECT DISTINCT FIRST_NAME FROM PEOPLE WHERE LAST_NAME IN ("Smith","Jones","Gupta")
then based on each FIRST_NAME returned I query
SELECT *
FROM PEOPLE
WHERE FIRST_NAME = {FIRST_NAME}
AND LAST_NAME IN ("Smith","Jones","Gupta")
ORDER BY FIELD(LAST_NAME, "Smith","Jones","Gupta") DESC
LIMIT 1
The "List of last names" serves as a "default / override" indicator, so I only have one person for each first name, and where multiple rows for the same first name exist, only the Last match from the list of "Last Names" is used.
I need a SQL query that returns the last row from the "in" clause based on the order of the values in the IN(a,b,c). Here is a sample table, and the results I need from the query.
For the Table PEOPLE, with values
LAST_NAME FIRST_NAME
.....
Smith Mike
Smith Betty
Smith Jane
Jones Mike
Jones Sally
....
I need a query based on DISTINCT FIRST_NAME and LAST_NAME IN ('Smith','Jones') that returns
Betty Smith
Jane Smith
Mike Jones
Sally Jones
You can do it like this:
select first_name, last_name
from (
select p.first_name,
p.last_name,
row_number() over (partition by p.first_name
order by case p.last_name
when 'Smith' then 1
when 'Jones' then 2
when 'Gupta' then 3
end desc) as rn
from people p
where p.last_name in ('Smith','Jones','Gupta')
)
where rn = 1;
Demo: SQL Fiddle
EDIT
It's not hard to get more columns. I'm sure you could have figured it out with a bit more effort:
select *
from (
select p.*,
row_number() over (partition by p.first_name
order by case p.last_name
when 'Smith' then 1
when 'Jones' then 2
when 'Gupta' then 3
end desc) as rn
from people p
where p.last_name in ('Smith','Jones','Gupta')
)
where rn = 1;
Or like this:
select first_name,
max(last_name)
keep (dense_rank first order by decode(last_name,
'Smith', 1,
'Jones', 2,
'Gupta', 3) desc)
group by first_name
Oracle "FIRST"/"LAST" functions allow to get values from other columns of row with maximum/minimum value (for example get last_name of employee with maximum salary, or like in this case - get last_name from row with maximum rank)
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions056.htm