I have two tables dept and work; values are like this
INSERT INTO DEPT(DEPTID, DEPTNAME) VALUES
( 1, 'DEPT1'),
(2, 'DEPT2'),
(3,'DEPT2'),
(4,'DEPT4'),
(5,'DEPT4'),
(6,'DEPT6');
INSERT INTO WORK (WORKID, DEPTID, SAL,CITYID) VALUES
( 100,1,1000,10),
( 100,2,2000,10),
( 200,1,2500,20),
( 300,3,1000,10),
( 300,6,3000,10),
( 300,6,1000,30)
;
For the given cityid ( work table), take all deptids for each deptname (from dept table ) and match in work table, if exists display count (count of deptids present in work table) and avgsal else 0 for count and avgsal all dept names in the same order present in dept table. Output should be like this
Cityid deptname count avg-sal
10 dept1 1 1000
10 dept2 2 1500
10 dept4 0 0
10 dept6 1 3000
Grandtotal 3 2333.33
I tried thru joins, but didnt work properly
You have a very strange department table -- containing duplicates.
In any case, this is a join and group by:
select 10 as cityid, coalesce(d.deptname, 'Total') as name,
count(w.workid) as cnt,
avg(sal) as average_salary
from dept d left join
work w
on w.deptid = d.deptid and w.cityid = 10
group by grouping sets ((w.cityid, d.deptname) ());
Here is a db<>fiddle.
Note that this returns the average salary as the average over the whole population. This is usually what one wants. The value is 1750, which looks correct to me.
Related
I have two tables
EMP
DEPT
The structure of DEPT
DEPT_ID
DNAME
10
ACCOUNT
20
ADMIN
The structure of EMP
EMP_ID
ENAME
DEPT_ID
1
JOHN
10
2
VICK
20
3
WILL
20
The other 2 tables, say NEW_EMP and NEW_DEPT also have the similar relation and columns
Here, the NEW_DEPT's dept_id is driven by a sequence (schema.sequence.nextval)
To migrate the first table, I have done
INSERT INTO NEW_DEPT(dept_id, dname)
(SELECT schema.sequence.nextval, dname||'_'||dept_id FROM DEPT )
So, now the data of NEW_DEPT would be: assuming the next val of sequence as 101.
DEPT_ID
DNAME
101
ACCOUNT_10
102
ADMIN_20
How can I now relate the NEW_EMP with the NEW_DEPT and insert EMP table rows ?
"Trying to achieve the insert of NEW_EMP"
Result of NEW_EMP
EMP_ID
ENAME
DEPT_ID
1
JOHN
101
2
VICK
102
3
WILL
102
Not sure why you are generating new id's, what's wrong with the old ones? Anyhow, you need to keep track of the relationship between the old and the new id. One way is to create a map table:
CREATE TABLE map_dept_id
( dept_id ... not null primary key
, new_dept_id ... not null unique
);
Then create the content for this table as:
INSERT INTO map_dept_id (dept_id, new_dept_id)
SELECT schema.sequence.nextval, dept_id FROM DEPT;
Now you can use this table when migrating your departments as:
INSERT INTO new_dept (dept_id, dname)
SELECT m.new_dept_id, d.dname||'_'||m.dept_id
FROM map_dept_id m
JOIN dept d
ON m.dept_id = d.dept_id
Use a similar technique for mapping emp's old dept_id to their new one.
It's usually wise to keep the map table for a period of time before dropping it.
I have Two tables that contain employees.
One table has all active employees (Current_Employees) and the other one has employees that have been joined in the last month (Greenhouse_Employees). Sometimes these employees overlap.
I have a unqiue ID (position_ID) that I want to do the following with in the abstract:
If Greenhouse_Employees unique ID exists in or matches to an ID in Current_Employees, ignore, if it does not: append it and its associated columns to the table
Not all of the columns match in either table, some do.
The code below almost works, but if there is a single inconsistency any any column I coalesce, it duplicates the row: (Some employees have inconsistent [loc] (locations) in the tables due to data entry error
SELECT
COALESCE(#CURRENT_EMPLOYEES.[employee_status], [GREENHOUSE_TABLE].[job_status]) AS employment_status
,COALESCE(#CURRENT_EMPLOYEES.[employee_id], 'N/A') AS employee_id
,COALESCE(#CURRENT_EMPLOYEES.[employee_name], CONCAT([GREENHOUSE_TABLE].[first_name],' ',[GREENHOUSE_TABLE].[last_name])) AS employee_name
,COALESCE(#CURRENT_EMPLOYEES.[hire_date], [GREENHOUSE_TABLE].[hire_date]) AS hire_date
,COALESCE(#CURRENT_EMPLOYEES.[salary], [GREENHOUSE_TABLE].[salary]) AS salary
,COALESCE(#CURRENT_EMPLOYEES.[bonus_percent], [GREENHOUSE_TABLE].[annual_bonus]) AS bonus_percent
,COALESCE(#CURRENT_EMPLOYEES.[commission_percent], '0') AS commission_percent
,COALESCE(#CURRENT_EMPLOYEES.[currency], 'N/A') AS currency
,COALESCE(#CURRENT_EMPLOYEES.[company_title], [GREENHOUSE_TABLE].[company_title]) AS company_title
,COALESCE(#CURRENT_EMPLOYEES.[company_department], [GREENHOUSE_TABLE].[company_department]) AS company_department
,COALESCE(#CURRENT_EMPLOYEES.[country], 'N/A') AS country
,COALESCE(#CURRENT_EMPLOYEES.[loc], 'N/A') AS loc
,COALESCE(#CURRENT_EMPLOYEES.[job_level], [GREENHOUSE_TABLE].[job_level]) AS job_level
,COALESCE(#CURRENT_EMPLOYEES.[kamsa_code], [GREENHOUSE_TABLE].[kamsa_code]) AS kamsa_code
,COALESCE(#CURRENT_EMPLOYEES.[position_id],[GREENHOUSE_TABLE].[position_id]) AS position_id
FROM #CURRENT_EMPLOYEES
FULL JOIN [Headcount].[dbo].[greenhouse_employees] AS GREENHOUSE_TABLE ON #CURRENT_EMPLOYEES.[position_id] = [GREENHOUSE_TABLE].[position_id]
ORDER BY #CURRENT_EMPLOYEES.[hire_date] ASC
I believe you need to You need to INSERT ... SELECT ... WHERE NOT EXISTS(...) or INSERT ... SELECT ... WHERE <Id> NOT IN (...). Something like:
INSERT #CURRENT_EMPLOYEES (employee_status, employee_id, ...)
SELECT employee_status, employee_id, ...
FROM Headcount.dbo.greenhouse_employees GE
WHERE NOT EXISTS (
SELECT *
FROM #CURRENT_EMPLOYEES CE
WHERE CE.employee_id = GE.employee_id
)
The other form is
INSERT #CURRENT_EMPLOYEES (employee_status, employee_id, ...)
SELECT employee_status, employee_id, ...
FROM Headcount.dbo.greenhouse_employees GE
WHERE GE.employee_id NOT IN (
SELECT CE.employee_id
FROM #CURRENT_EMPLOYEES CE
)
Both assume that employee_id is unique in greenhouse_employees.
I have a cursor with a left join like so:
CURSOR c_emp
SELECT * FROM FROM EMP_NEW
LEFT JOIN
(SELECT EMP_ID,
DEPT_ID,
SUB_DEPT_ID,
EMP_UID,
FROM EMP)
USING (DEPT_ID, SUB_DEPT_ID, EMP_UID);
Which is then referenced in a for loop:
For i in c_emp
IF i.emp_id IS NULL
then
insert into emp...
else
update emp...
where emp_id = i.emp_id;
end if;
We have a scenario when loading new data into EMP table from the EMP_NEW table, the emp_new.dept_id and emp_new.emp_uid can have a duplicate in the EMP table with a different sub_dept_id value. When we get such new data, the emp.emp_id is not selected in the cursor, thus logic goes to INSERT instead of UPDATE. I would like for the duplicate emp_id to be selected so it goes to the UPDATE.
EMP data:
EMP_ID DEPT_ID SUB_DEPT_ID EMP_UID
123 1 10 123.123
EMP_NEW data:
DEPT_ID SUB_DEPT_ID EMP_UID
1 20 123.123
The above cursor currently returns the following:
EMP_ID DEPT_ID SUB_DEPT_ID EMP_UID
null 1 20 123.123
What I would like it to return:
EMP_ID DEPT_ID SUB_DEPT_ID EMP_UID
123 1 20 123.123
You seem to want to match on the 3 columns first and if not on just two columns. I think a lateral join better fits your needs:
SELECT en.*, e.*
FROM EMP_NEW en LEFT JOIN LATERAL
(SELECT EMP_ID
FROM EMP e
WHERE en.DEPT_ID = e.DEPT_ID AND en.EMP_UID = e.EMP_UID
ORDER BY (CASE WHEN en.SUB_DEPT_ID = e.SUB_DEPT_ID THEN 1 ELSE 2 END)
FETCH FIRST 1 ROW ONLY -- presumably you want only one row either way
) e
I have a table like this
EmpId No_of_days
1 24
2 28
6 24
7 30
8 10
9 15
when I write a select statement
SELECT No_of_days FROM _table WHERE EmpId = 3 or 4 or 5
I got an empty result set.
I want the result set return 0 days for employees 3 and 4 and 5
You can use a values() construct & do left join :
select tt.empid, isnull(t.No_of_days, 0)
from ( values (3), (4), (5)
) tt (empid) left join
table t
on t.empid = tt.empid;
EDIT : If you have a more employees id then you have to maintain one staging table & do left join :
select tt.empid,
ISNULL(t.No_of_days, 0)
from table tt left join -- possible employee id table
[_table] t
on t.empid = tt.empid;
Assuming that you have another table with all your employees in (if not, where are employees 3, 4 and 5 coming from?), you could use a LEFT JOIN onto your example table (_table):
SELECT e.empid,
ISNULL(t.No_of_days,0)
FROM Employee e --No idea what your employee table is actually called, so guessed.
LEFT JOIN _table t ON e.empid = t.empid;
This will provide rows for all your employees. If you wanted just 3, 4 and 5, then you would add your WHERE:
WHERE e.empid IN (3,4,5)
use coalesce
select coalesce(No_of_days,0)
from _table WHERE EmpId in( 3 , 4 , 5)
BTW use In instead or and if you have to push those id in EmpId 1st then
coalesce() will work otherwise if no values in empid then it will not work
I want to show the pivot table(crosstab) for the given below table.
Table: Employee
CREATE TABLE Employee
(
Employee_Number varchar(10),
Employee_Role varchar(50),
Group_Name varchar(10)
);
Insertion:
INSERT INTO Employee VALUES('EMP101','C# Developer','Group_1'),
('EMP102','ASP Developer','Group_1'),
('EMP103','SQL Developer','Group_2'),
('EMP104','PLSQL Developer','Group_2'),
('EMP101','Java Developer',''),
('EMP102','Web Developer','');
Now I want to show the pivot table for the above data as shown below:
Expected Result:
Employee_Number TotalRoles TotalGroups Available Others Group_1 Group_2
---------------------------------------------------------------------------------------------------
EMP101 2 2 1 1 1 0
EMP102 2 2 1 1 1 0
EMP103 1 2 1 0 0 1
EMP104 1 2 1 0 0 1
Explanation: I want to show the Employee_Number, the TotalRoles which each employee has,
the TotalGroups which are present to all employees, the Available shows the employee available
in how many groups, the Others have to show the employee is available in other's also for which
the group_name have not assigned and finally the Group_Names must be shown in the pivot format.
SELECT * FROM crosstab(
$$SELECT grp.*, e.group_name
, CASE WHEN e.employee_number IS NULL THEN 0 ELSE 1 END AS val
FROM (
SELECT employee_number
, count(employee_role)::int AS total_roles
, (SELECT count(DISTINCT group_name)::int
FROM employee
WHERE group_name <> '') AS total_groups
, count(group_name <> '' OR NULL)::int AS available
, count(group_name = '' OR NULL)::int AS others
FROM employee
GROUP BY 1
) grp
LEFT JOIN employee e ON e.employee_number = grp.employee_number
AND e.group_name <> ''
ORDER BY grp.employee_number, e.group_name$$
,$$VALUES ('Group_1'::text), ('Group_2')$$
) AS ct (employee_number text
, total_roles int
, total_groups int
, available int
, others int
, "Group_1" int
, "Group_2" int);
SQL Fiddle demonstrating the base query, but not the crosstab step, which is not installed on sqlfiddle.com
Basics for crosstab:
PostgreSQL Crosstab Query
Special in this crosstab: all the "extra" columns. Those columns are placed in the middle, after the "row name" but before "category" and "value":
Pivot on Multiple Columns using Tablefunc
Once again, if you have a dynamic set of groups, you need to build this statement dynamically and execute it in a second call:
Selecting multiple max() values using a single SQL statement
You can use the crosstab function for this.
First of all you need to add the tablefunc extension if you haven't already:
CREATE EXTENSION tablefunc;
The crosstab functions require you to pass it a query returning the data you need to pivot, then a list of the columns in the output. (In other ways "tell me the input and the output format you want"). The sort order is important!
In your case, the input query is quite complicated - I think you need to do a load of separate queries, then UNION ALL them to get the desired data. I'm not entirely sure how you calculate the values "TotalGroups" and "Available", but you can modify the below in the relevant place to get what you need.
SELECT * FROM crosstab(
'SELECT employee_number, attribute, value::integer AS value FROM (with allemployees AS (SELECT distinct employee_number FROM employee) -- use a CTE to get distinct employees
SELECT employee_number,''attr_0'' AS attribute,COUNT(distinct employee_role) AS value FROM employee GROUP BY employee_number -- Roles by employee
UNION ALL
SELECT employee_number,''attr_1'' AS attribute,value from allemployees, (select count (distinct group_name) as value from employee where group_name <> '''') a
UNION ALL
SELECT employee_number,''attr_2'' AS attribute, COUNT(distinct group_name) AS value FROM employee where group_name <> '''' GROUP BY employee_number -- Available, do not know how this is calculate
UNION ALL
SELECT a.employee_number, ''attr_3'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- other groups. Use a LEFT JOIN to avoid nulls in the output
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name ='''' GROUP BY employee_number) b on a.employee_number = b.employee_number
UNION ALL
SELECT a.employee_number, ''attr_4'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- group 1
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name =''Group_1'' GROUP BY employee_number) b on a.employee_number = b.employee_number
UNION ALL
SELECT a.employee_number, ''attr_5'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- group 2
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name =''Group_2'' GROUP BY employee_number) b on a.employee_number = b.employee_number) a order by 1,2')
AS ct(employee_number varchar,"TotalRoles" integer,"TotalGroups" integer,"Available" integer, "Others" integer,"Group_1" integer, "Group_2" integer)