Combining tables in SQL/QlikView - sql

Is it possible to combine 2 tables with a join or similar construct so that all non matching field in one group. Some thing like this:
All employees with a department name gets their real department and all with no department ends up in group "Other".
Department:
SectionDesc ID
Dep1 500
Dep2 501
Employee:
Name ID
Anders 500
Erik 501
root 0
Output:
Anders Dep1
Erik Dep2
root Other
Best Regards Anders Olme

What you are looking for is an outer join:
SELECT e.name, d.name
FROM employee e
LEFT OUTER JOIN departments d ON e.deptid = d.deptid
This would give you a d.name of NULL for every employee without a department. You can change this to 'Other' with something like this:
CASE WHEN d.name IS NULL THEN 'Other' Else d.name END
(Other, simpler versions for different DBMSs exist, but this should work for most.)
QlikView is a bit tricky, as all joins in QlikView are inner joins by default. There is some discussion in the online help about the different joins, short version is that you can create a new table based on different joins in the script that reads in your data. So you could have something like this in your script:
Emps: SELECT * FROM EMPLOYEES;
Deps: SELECT * FROM DEPARTMENTS;
/* or however else you get your data into QlikView */
EmpDep:
SELECT Emps.name, Deps.name
FROM EMPS LEFT JOIN Deps
In order for this join to work the column names for the join have to be the same in both tables. (If necessary, you can construct new columns for the join when loading the base tables.)

Related

INNER JOIN and Count POSTGRESQL

I am learning postgresql and Inner join I have following table.
Employee
Id Name DepartmentId
1 John S. 1
2 Smith P. 1
3 Anil K. 2
Department
Department
Id Name
1 HR
2 Admin
I want to query to return the Department Name and numbers of employee in each department.
SELECT Department.name , COUNT(Employee.id) FROM Department INNER JOIN Employee ON Department.Id = Employee.DepartmentId Group BY Employee.department_id;
I dont know what I did wrong as I am new to database Query.
When involving all rows or major parts of the "many" table, it's typically faster to aggregate first and join later. Certainly the case here, since we are after counts for "each department", and there is no WHERE clause at all.
SELECT d.name, COALESCE(e.ct, 0) AS nr_employees
FROM department d
LEFT JOIN (
SELECT department_id AS id, count(*) AS ct
FROM employee
GROUP BY department_id
) e USING (id);
Also made it a LEFT [OUTER] JOIN, to keep departments without any employees in the result. And COALESCE to report 0 employees instead of NULL in that case.
Related, with more explanation:
Query with LEFT JOIN not returning rows for count of 0
Your original query would work too, after fixing the GROUP BY clause:
SELECT department.name, COUNT(employee.id)
FROM department
INNER JOIN employee ON department.id = employee.department_id
Group BY department.id; --!
That's assuming department.id is the PRIMARY KEY of the table, in which case it covers all columns of that table, including department.name. And you may want LEFT JOIN like above.
Aside: Consider legal, lower-case names exclusively in Postgres. See:
Are PostgreSQL column names case-sensitive?

SQL Server : replace several instances of userID with username

I may be overthinking this but I have not managed to figure it out or find a solution, so I'm hoping for a pointer in the right direction. I tried using the Select ColumnA AS Column B etc but it's not doing what I want.
I have 2 tables, scenario examples below
Table 1 (Vehicle)
VehicleID (001)
VehicleMake (Ford)
VehicleModel (Falcon)
VehicleExCleanEmpID (005)
VehicleIntCleanEmpID (003)
Table 2 (Employee)
EmpID (005)
EmpName (Dave)
The scenario being that a vehicle is cleaned internally or externally by any one of a pool of employees shown by the relevant ID in the Vehicles table.
I want to show in a query VehicleID, InsideCleanName, ExternalCleanName rather than showing the employee's ID.
So end up with results similar to this
VehicleID InsideCleanName ExternalCleanName
------------------------------------------------
001 Bob Dave
002 Sue Dave
003 John Sid
Thanks for any tips and or help
THat seems like a pretty simple query with two inner joins to the Employee table - something like this:
SELECT
v.VehicleId,
InsideCleanName = e1.EmpName,
ExternalCleanName = e2.EmpName
FROM
dbo.Vehicle v
INNER JOIN
dbo.Employee e1 ON v.VehicleIntCleanEmpId = e1.EmpID
INNER JOIN
dbo.Employee e2 ON v.VehicleExCleanEmpId = e2.EmpID
Joining to the Employee e1 table is giving you the employee who was responsible for the inside cleaning, while joining a second time, to Employee e2 gives you the one responsible for the external cleaning.
Join the two table with EmpId and select the columns you want similar to the code below:
select column1, column2 from table1 inner join table2 on table1.EmpId = table2.EmpId

Representing 'not in' subquery as join

I am trying to convert the following query:
select *
from employees
where emp_id not in (select distinct emp_id from managers);
into a form where I represent the subquery as a join. I tried doing:
select *
from employees a, (select distinct emp_id from managers) b
where a.emp_id!=b.emp_id;
I also tried:
select *
from employees a, (select distinct emp_id from managers) b
where a.emp_id not in b.emp_id;
But it does not give the same result. I have tried the 'INNER JOIN' syntax as well, but to no avail. I have become frustrated with this seemingly simple problem. Any help would be appreciated.
Assume employee Data set of
Emp_ID
1
2
3
4
5
6
7
Assume Manger data set of
Emp_ID
1
2
3
4
5
8
9
select *
from employees
where emp_id not in (select distinct emp_id from managers);
The above isn't joining tables so no Cartesian product is generated... you just have 7 records you're looking at...
The above would result in 6 and 7 Why? only 6 and 7 from Employee Data isn't in the managers table. 8,9 in managers is ignored as you're only returning data from employee.
select *
from employees a, (select distinct emp_id from managers) b
where a.emp_id!=b.emp_id;
The above didnt' work because a Cartesian product is generated... All of Employee to all of Manager (assuming 7 records in each table 7*7=49)
so instead of just evaluating the employee data like you were in the first query. Now you also evaluate all managers to all employees
so Select * results in
1,1
1,2
1,3
1,4
1,5
1,8
1,9
2,1
2,2...
Less the where clause matches...
so 7*7-7 or 42. and while this may be the answer to the life universe and everything in it, it's not what you wanted.
I also tried:
select *
from employees a, (select distinct emp_id from managers) b
where a.emp_id not in b.emp_id;
Again a Cartesian... All of Employee to ALL OF Managers
So this is why a left join works
SELECT e.*
FROM employees e
LEFT OUTER JOIN managers m
on e.emp_id = m.emp_id
WHERE m.emp_id is null
This says join on ID first... so don't generate a Cartesian but actually join on a value to limit the results. but since it's a LEFT join return EVERYTHING from the LEFT table (employee) and only those that match from manager.
so in our example would be returned as e.emp_Di = m.Emp_ID
1,1
2,2
3,3
4,4
5,5
6,NULL
7,NULL
now the where clause so
6,Null
7,NULL are retained...
older ansii SQL standards for left joins would have been *= in the where clause...
select *
from employees a, managers b
where a.emp_id *= b.emp_id --I never remember if the * is the LEFT so it may be =*
and b.emp_ID is null;
But I find this notation harder to read as the join can get mixed in with the other limiting criteria...
Try this:
select e.*
from employees e
left join managers m on e.emp_id = m.emp_id
where m.emp_id is null
This will join the two tables. Then we discard all rows where we found a matching manager and are left with employees who aren't managers.
Your best bet would probably be a left join:
select
e.*
from employees e
left join managers m on e.emp_id = m.emp_id
where
m.emp_id is null;
The idea here is you're saying that you want to select everything from employees, including anything that matches in the manager table based on emp_id and then filtering out the rows that actually have something in the manager table.
Use Left Outer Join instead
select e.*
from employees e
left outer join managers m
on e.emp_id = m.emp_id
where m.emp_id is null
left outer join will preserve the rows from m table even if they do not have a match i e table based on the emp_id field. The we filter on where m.emp_id is null - give me all the rows from e where there's no matching record in m table.
A bit more on the subject can be found here:
Visual representation of joins
from employees a, (select distinct emp_id from managers) b implies cross join - all posible combinations between tables (and you needed left outer join instead)
The MINUS keyword should do the trick:
SELECT e.* FROM employees e
MINUS
Select m.* FROM managers m
Hope that helps...
select *
from employees
where Not (emp_id in (select distinct emp_id from managers));

Joining same table multiple times

I have 2 tables Person an Department - where each person has multiple departments registered against him.
Person
id|name|dept1|dept2|dept3
1 |Jane|100 |102 |106
Dept
id |Name
100|Accounts
...
102|HR
...
106|Admin
Whats the most elegant sql to display Jane's record as follows:
Jane|Accounts|HR|Admin
Use this. And work on your naming convention to make all column names unique independent of table.
SELECT
Person.id, Person.name, dept1.Name, dept2.Name, dept3.Name
LEFT JOIN Dept dept1 ON dept1.id = Person.dept1
LEFT JOIN Dept dept2 ON dept2.id = Person.dept2
LEFT JOIN Dept dept3 ON dept3.id = Person.dept3
Something like this will let you join the same table multiple times. You just give each join a different alias
SELECT *
FROM Person AS P
INNER JOIN Dept AS D1 ON P.dept1 = D1.id
INNER JOIN Dept AS D2 ON P.dept2 = D2.id
WHERE P.name = 'Jane'
Ideally you would normalise the data in your Person table and have a linking table between Person and Dept e.g. PersonDepartmentLinking (or whatever convention you have for linking table naming conventions), assuming you have any control over the schema and it's possible to add the relationship that way.
You can use STUFF in this case
Select name,
Stuff((Select distinct ', ' + cast(Name as varchar(20))
From #Dept t2
Where t2.Id = t1.Id
FOR XML PATH('')),1,1,'')
From Person t1
The only way that I know is a join for each column.
But if you are in the way of designing the tables, i suggest you to normalize the DB.
If a person need an extra dept column, you need to alter the table to add a new property of person.
For me, 3 entities are needed:
- Person
- Department
- person_department_assignation

Minimizing SQL queries using join with one-to-many relationship

So let me preface this by saying that I'm not an SQL wizard by any means. What I want to do is simple as a concept, but has presented me with a small challenge when trying to minimize the amount of database queries I'm performing.
Let's say I have a table of departments. Within each department is a list of employees.
What is the most efficient way of listing all the departments and which employees are in each department.
So for example if I have a department table with:
id name
1 sales
2 marketing
And a people table with:
id department_id name
1 1 Tom
2 1 Bill
3 2 Jessica
4 1 Rachel
5 2 John
What is the best way list all departments and all employees for each department like so:
Sales
Tom
Bill
Rachel
Marketing
Jessica
John
Pretend both tables are actually massive. (I want to avoid getting a list of departments, and then looping through the result and doing an individual query for each department). Think similarly of selecting the statuses/comments in a Facebook-like system, when statuses and comments are stored in separate tables.
You can get it all in a single query with a simple join, e.g.:
SELECT d.name AS 'department', p.name AS 'name'
FROM department d
LEFT JOIN people p ON p.department_id = d.id
ORDER BY department
This returns all the data, but it's a bit of a pain to consume, since you'll have to iterate through every person anyway. You can go further and group them together:
SELECT d.name AS 'department',
GROUP_CONCAT(p.name SEPARATOR ', ') AS 'name'
FROM department d
LEFT JOIN people p ON p.department_id = d.id
GROUP BY department
You'll get something like this as the output:
department | name
-----------|----------------
sales | Tom, Bill, Rachel
marketing | Jessica, John
SELECT d.name, p.name
FROM department d
JOIN people p ON p.department_id = d.id
I suggest also reading a SQL Join tutorial or three. This is a very common and very basic SQL concept that you should understand thoroughly.
This is normally done in a single query:
SELECT DepartmentTable.Name, People.Name from DepartmentTable
INNER JOIN People
ON DepartmentTable.id = People.department_id
ORDER BY DepartmentTable.Name
This will suppress empty departments. If you want to show empty departments, change INNER to LEFT OUTER