Need help to get correct sql query - sql

I have a table Employee, one of its attribute is 'Gender'. In Gender column we have two type of records 'male' or 'female'
Now i suppose to write a query which should give me an output like 1st record should be for 'Male', 2nd record for 'Female', 3rd for 'Male',
4th for 'Female'.
I have used below query to fetch record as above mentioned
select name, empid, salary, gender, rownum rn, case gender when 'Male' then
rn = (select * from (select rownum rn from Employee) where mod (rn, 2) <> 0)
else rn = (select * from (select rownum rn from Employee) where mod (rn, 2) = 0) end as Org_Gender form employee;
but this query not able to fetch the required output.
Can someone give me the syntax please.?

This should do it:
select empid, name, gender
from (
select name, empid, gender,
row_number() over (partition by gender order by name) as rn
from employee
) t
order by rn, gender
row_number() over (partition by gender ..) will number all females from 1 to x and all males from 1 to x. By ordering the outer query using that value the final output will have the first female, then the first male, the second female, the second male and so on.
SQLFiddle example: http://sqlfiddle.com/#!4/23f656/2

Related

Is this SQL query possible? I am trying to get the least frequent names in this table

This is a toy public table in google BigQuery:
The table contains the names given to people in the US at birth and the frequency of those names for each state and year from 1910 to 2020
Columns: name, year, state, number, gender
names toy table
I am trying to get the LEAST popular names (names with lowest 'number' column) each year.
I am not sure this is possible with this schema.
Depending on how you want to handle a tie, you want rank or row_number.
select
*
from (
select
*,
row_number() over (partition by year order by name_frequency) as rn
from (
select
year,
name,
sum(number) as name_frequency
from `bigquery-public-data.usa_names.usa_1910_2013`
group by
year,
name
) sub1
) sub2
where rn = 1
Consider below approach
select * from (
select year, gender, name, sum(number) number
from `bigquery-public-data.usa_names.usa_1910_2013`
group by year, gender, name
qualify 1 = row_number() over(partition by year, gender order by number)
)
pivot (any_value(name) name, any_value(number) number for gender in ('M', 'F'))
# order by year desc
with output (just top 9 shown)
In reality - there are many names that have same least frequency - to get all of them - use below approach
select * from (
select year, gender, name, sum(number) number
from `bigquery-public-data.usa_names.usa_1910_2013`
group by year, gender, name
qualify 1 = dense_rank() over(partition by year, gender order by number)
)
pivot (string_agg(name) name, any_value(number) number for gender in ('M', 'F'))
with output
while if you would look for most frequent - you would use below
select * from (
select year, gender, name, sum(number) number
from `bigquery-public-data.usa_names.usa_1910_2013`
group by year, gender, name
qualify 1 = dense_rank() over(partition by year, gender order by number desc)
)
pivot (string_agg(name) name, any_value(number) number for gender in ('M', 'F'))
with just one most frequent name per year/gender

Oracle SQL - Get name from table based on group by

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.

How to eliminate duplicate records based on only few columns in table and keep one and indicate that there were duplicate records corresponding to it?

The resultset I have is like shown below:
And expected output is like shown below:
Any idea how can we achieve this with SQL in Oracle?
You can use window functions:
select city, name, salary,
(case when cnt > 1 then 'Multiple' else 'Single' end) as Indicator
from (select t.*,
count(*) over (partition by city, name) as cnt,
row_number(*) over (partition by city, name order by salary) as seqnum
from t
) t
where seqnum = 1;
EDIT:
Actually, if you want the minimum salary:
select city, name, min(salary),
(case when count(*) = 1 then 'Single' else 'Multiple' end) as indicator
from t
group by city, name;
Try this
DELETE FROM tabename WHERE rowid in
(SELECT city, name, salary,COUNT(*)
FROM tabename
GROUP BY city, name, salary
HAVING count(*) > 1);

conditional order by clause in sql

I have a query that should order the result in asc or desc depending upon a column value.
e.g.
if employee of type manager exists THEN order by joining_date, bith_date ASC
else if employee is developer THEN order by joining_date, birth_date DESC.
I would like to achieve something like below, but can't achieve that.
ORDER BY CASE WHEN employee_type = 'm'
THEN joining_date, birth_date ASC;
WHEN employee_type = 'd'
THEN joining_date, birth_date DESC;
Well I got the answer after some research.
We can add multiple columns in where clause conditionally as follows :
ORDER BY DECODE(employee_type, 'm', joining_date, birth_date, salary) ASC,
DECODE(employee_type, 'd', joining_date, birth_date, salary) DESC
This will order the result on the basis of employee_type.
I suspect you want something like this:
ORDER BY
employee_type DESC -- first all the managers, then the developers
-- and in every one of these two groups
, joining_date -- first order by joining date
, CASE WHEN employee_type = 'm' -- and then either by
THEN birth_date -- birth date ascending for managers
ELSE NULL
END -- or
, birth_date DESC ; -- birth date descending for the rest (devs)
The question is a little bit poor specified.
order the result in asc or desc depending upon a column value.
A column takes many values (as there are multiple rows).
Now, order by clause use an expression and order rows upon it.
That expression should be morphotropic(;))
So, assuming stardard oracle's employee schema, managers are:
select *
from emp e
where exists (select emp_id from emp where e.id=emp.mgr_id)
An workaround query may be:
Select e.id, e.name, e.birth_date,
case
when (select count(*)
from emp e
where exists (select emp_id from emp where e.id=emp.mgr_id)
) --existence of manager
> 0 then birth_date - to_date('1-Jan-1000','dd-mon-yyyy')
else to_date('1-Jan-1000','dd-mon-yyyy') - birth_date
end as tricky_expression
from emp A
order by 4;
That exexpresion is the case; Using a constant(subquery that decides there are managers) it changes values from positive to negative, that is, change the order direction.
UPDATE: with the details in the comments:
select id, name, birth_date emp_type
from (
Select id, name, birth_date, emp_type,
case when cnt_mgr > 0 then birth_date - to_date('1-Jan-1000','dd-mon-yyyy')
else to_date('1-Jan-1000','dd-mon-yyyy') - birth_date
end as tricky_expression
from(
Select e.id, e.name, e.birth_date, emp_type,
count(case when emp_type='M' then 1 else 0 end) over() as mgr_count
from emp A
where your_conditions
)
order by tricky_expression
)
where rownum=1;
If there is a manager in the company this query returns the oldest manager, otherwise - the youngest developer.
select
id, name, birth_date, emp_type
from emp
where
id = (select
max(id) keep (dense_rank first order by
decode(emp_type, 'M', 1, 'D', 2),
joining_date,
decode(emp_type, 'M', 1, 'D', -1) * (birth_date - to_date('3000','yyyy')))
from emp)

SQl server query multiple aggregate columns

I need to write a query in sql server to data get like this.
Essentially it is group by dept, race, gender and then
SUM(employees_of_race_by_gender),Sum(employees_Of_Dept).
I could get data of first four columns, getting sum of employees in that dept is becoming difficult.
Could you pls help me in writing the query?
All these details in same table Emp. Columns of Emp are Emp_Number, Race_Name,Gender,Dept
Your "num_of_emp_in_race" is actually by Gender too
SELECT DISTINCT
Dept,
Race_name,
Gender,
COUNT(*) OVER (PARTITION BY Dept, Race_name, Gender) AS num_of_emp_in_race,
COUNT(*) OVER (PARTITION BY Dept) AS num_of_emp_dept
FROM
MyTable
You should probably have this
COUNT(*) OVER (PARTITION BY Dept, Gender) AS PerDeptRace
COUNT(*) OVER (PARTITION BY Dept, Race_name) AS PerDeptGender,
COUNT(*) OVER (PARTITION BY Dept, Race_name, Gender) AS PerDeptRaceGender,
COUNT(*) OVER (PARTITION BY Dept) AS PerDept
Edit: the DISTINCT appears to be applied before the COUNT (which would odd based on this) so try this instead
SELECT DISTINCT
*
FROM
(
SELECT
Dept,
Race_name,
Gender,
COUNT(*) OVER (PARTITION BY Dept, Race_name, Gender) AS num_of_emp_in_race,
COUNT(*) OVER (PARTITION BY Dept) AS num_of_emp_dept
FROM
MyTable
) foo
Since the two sums you're looking for are based on a different aggregation, you need to calculate them separately and join the result. In such cases I first build the selects to show me the different results, making it easy to catch errors early:
SELECT Dept, Gender, race_name, COUNT(*) as num_of_emp_in_race
FROM Emp
GROUP BY 1, 2, 3
SELECT Dept, COUNT(*) as num_of_emp_in_dept
FROM Emp
GROUP BY 1
Afterwards, joining those two is pretty straight forward:
SELECT *
FROM ( first statement here ) as by_race
JOIN ( second statement here ) as by_dept ON (by_race.Dept = by_dept.Dept)