One to many relationships in T-SQL - sql

I need to figure out how to find a certain group of records using T-SQL and I'm having trouble figuring out how I would need to create the WHERE clause to do this.
I have a SQL 2008 R2 system that I'm working with, and in this database there are a couple of tables. One contains personnel records, and another contains addresses. The addresses relate to the personnel records by a foreign key relationship. So for example, to get a list of all personnel and all of their associated addresses (a single person could have multiple addresses) I could write something like this:
SELECT id, name FROM personnel p
INNER JOIN address a
ON p.id = a.personnelid
However, each address has a column called isprimary, that is either 0 or 1. What I need to do is figure out how to find all personnel who do not have an associated address with isprimary set to 1. Or records that have no primary address.
Currently my thought is to build a temporary table with personnel who have addresses that aren't marked as primary. Then cycle through those and build a subset that have a primary address.
Then subtract the Personnel With Primary table from the results of Personnel With Non-Primary and I should have my list. However, I'm thinking that there has to be a more elegant way of doing this. Any ideas?

Try this, it should get all Personnel rows with no matching primary address:
SELECT *
FROM Personnel p
WHERE NOT EXISTS
(SELECT * FROM Address a WHERE a.personnelId = p.id AND a.isprimary = 1)

SELECT id, name FROM personnel p
INNER JOIN address a
ON p.id = a.personnelid
AND a.isprimary = 0

This ends up beeing a Left anti semi join pattern
and can be written like this:
SELECT id, name FROM personnel p
LEFT OUTER JOIN address a
ON p.id = a.personnelid
AND a.isprimary = 1
WHERE a.personnelId IS NULL
It can be interesting to test different ways because query plan are often not the same.

Related

PostgreSQL - copy column from related table

So I have three tables: companies, addresses and company_address.
For optimization reasons I need to copy city column from addresses table to companies table. Relation between companies and addresses is many to one (as many companies can occupy same address). They are connected through company_address table, consisting of address_id and company_id columns.
I found this solution for case without intermediate table: How to copy one column of a table into another table's column in PostgreSQL comparing same ID
Trying to modify query I came up with:
UPDATE company SET company.city=foo.city
FROM (
SELECT company_address.company_id, company_address.address_id, address.city
FROM address LEFT JOIN company_address
ON address.id=company_address.address_id
) foo
WHERE company.id=foo.company_id;
but it gives error:
ERROR: column "company" of relation "company" does not exist
I cant figure out what is going on. I'll be grateful for any ideas.
You don't need a subquery for that. Also, refer in the SET clause to your table columns without preceding with table name.
I believe that since your WHERE condition includes joined table, it should be INNER JOIN instead of a LEFT JOIN.
UPDATE company c
SET city = a.city
FROM address a
INNER JOIN company_address ca ON a.id = ca.address_id
WHERE c.id = ca.company_id
Note how using aliases for table names shortens the code and makes it readable at the very first glance.
You're right syntactically, you just don't need the table name at the beginning of the update statement:
UPDATE company SET city=foo.city
FROM (
SELECT company_address.company_id, company_address.address_id, address.city
FROM address LEFT JOIN company_address
ON address.id=company_address.address_id
) foo
WHERE company.id=foo.company_id;

SQL Query Display records only when value is missing

I am having an issue, I am new to SQL query work, but I have a query that runs and display employees and all their addresses history, but I have found that staff have been missing checking off the indicator for if the employee has mailing address. The addresses are stored in a table that has a reference to the employee id. How would I display results for a specific employee if no "2" value for mailing is found. The address table contains previous addresses and billing address flags, "1" and "3"?
In the addelement table
type_add_id|type_add_desc
1 |Billing
2 |Mailing
3 |Previous
Query
SELECT
addelement.type_add_desc
,address.street
,employee.name
FROM
address
INNER JOIN addelement
ON address.type_add_id = addelement.type_add_id
INNER JOIN employee
ON address.refid = employee.refid
order by employee.name
This will get you a list of employees that do not have a mailing address. Note that we start with all employees, outer join to the addresses, but constrain to not only match the employee, but also to be of the desired type of address. The WHERE clause then removes records from the resulting recordset where there is a value.
SELECT
employee.name
FROM
employee
LEFT OUTER JOIN address ON address.refid = employee.refid AND address.type_add_id = 2
WHERE
address.type_add_id IS NULL
Change the hardcoded integer in the JOIN to search out each of the desired types of addresses.
I think you should look at this http://sqlfiddle.com/#!9/e30116/1 and tell me what you think.
If the "2" is not there, it will simply give you the result of the other addresses.
Perhaps you'll make sense of this query easier than the one with a left join. It an also be confusing to understand why the filtering condition on the join must be with the join and not in the where clause. It'll be good for you to understand that eventually though if you continue learning SQL.
select *
from employee e
where not exists (
select 1
from address a
where a.refid = e.refid and a.type_add_id = 2
)
Many people seem to object to subqueries because their platforms don't optimize them well or because they see it as a crutch in using SQL. I suppose in a sense I am offering it here as an equivalent solution to help ramp up your understanding but either way I see nothing wrong with taking advantage of in and not in as a natural way of expression the solution.
This should return your employee #100 with all the addresses stored, even if employee has not address (any type).
select e.name, ad.type_add_desc, a.street
from employee e
left outer join address a on
e.refid = a.refid
left outer join addelement ad on
a.type_add_id = ad.type_add_id
where e.refid = 100
order by e.name;
To return rows that haven't a row relationship in other table you should use OUTER JOIN.

Query to find projects without leader

I'm doing a java application with a Postgres database and the following schema:
The entity employee, rol, project has some information inside,and the entity participants is empty. I want to show in my application a table of all projects with no leader assigned yet. I'm sure that#s possible with an SQL query but I'm not sure how. I tried this query:
SELECT p.projectnumber from participants pa, projecto p
where p.projectnumber=pa.projectnumber and pa.leaderid IS NULL;
But no rows are returned. That's because the participants entity is empty, but I cannot fill that entity with only the projectnumbers. Do you think I could make it easier with a query or well any other suggestion?
I want to show on my application a table that shows all the projects with no Leader assigned yet
Guessing that leaders are signified by having a non-null value in participants.leaderid:
SELECT projectnumber
FROM projecto p
WHERE NOT EXISTS (
SELECT 1
FROM participants
WHERE projectnumber = p.projectnumber
AND leaderid IS NOT NULL
);
You can solve it with a LEFT JOIN as well, but then include the leaderid in the join condition:
SELECT p.projectnumber
FROM projecto p
LEFT JOIN participants pa ON pa.projectnumber = p.projectnumber
AND pa.leaderid IS NOT NULL
WHERE pa.projectnumber IS NULL;
The check on leaderid in the WHERE condition (after the LEFT JOIN) cannot distinguish whether the column leaderid is NULL in the underlying table or because there is no connected row in participants at all. In this particular query, the result would still be correct (no participant, no leader). But it would return one row per participant that's not a leader, and I expect you want to list every leader-less project once only. You would have to aggregate, but why join to multiple non-leaders to begin with?
Basics:
Select rows which are not present in other table
That aside, your relational design doesn't seem to add up. What's to prevent multiple leaders for the same project? Why varchar(30) for most columns? Why no FK constraint between participant and project? Why projecto in the query, but project in the ER diagram? Etc.
You can use left join assuming projects which don't have entries in Participants table will be without leader:
SELECT p.projectnumber
FROM projecto p LEFT JOIN participants pa
ON p.projectnumber=pa.projectnumber
WHERE pa.leaderid IS NULL;

Access SQL Query on same table

I have two tables: one called EMP_Names which simply stores ID and Employee_Name and another table called EMP_Main which stores the main data and which refers to EMP_Names via IDs. Amongst other fields EMP_Main has fields called Technician_Name_ID and Leader_Name_ID which is related to EMP_Names. My problem is this: how can i run a query where both Technician_Name_ID and Leader_Name_ID resolve to Names? In other words both ID fields refer to the same EMP_Names.ID but I can only establish one relationship between the two tables.
Don't know if I'm clear because it's difficult to explain ...
You can use join but you need multiple joins.
select em.*, ent.name as technician, enl.name as leader
from (emp_main as em left join
emp_names as ent
on em.technician_name_id = ent.id
) left join
emp_names as enl
on em.leader_name_id = enl.id;
These are left joins in case the fields are not populated for all rows.

Start Center Result Set for People Table

I am working with the Maximo Asset Management System (version 7.1.1.6). I am trying to display a result set on the start center that contains a Do Not Call list of specific people. However, when using a query for the result set (a query that was saved in the People section that contains the appropriate "where" clause such as "department='ABC'"), I cannot select the phone number or the email address as a column to display. I believe this is due to the fact that the "Primary Phone" and "Primary Email" fields in the person table are not really there. They are virtual fields that are connected in the People application to the Phone and Email tables and joined on the personid column. If I run the following query in the database, I get the result set that I want:
select * from dbo.person as p
left outer join dbo.phone as ph on p.personid=ph.personid and ph.isprimary=1
left outer join dbo.email as e on p.personid=e.personid and e.isprimary=1
Unfortunately for the result sets, you don't have access to the "FROM" clause you can only edit the "WHERE" clause.
Anyone have any idea how to solve this other than adding 2 new columns to the person table for primary phone and primary email? I don't want to HAVE to do it, but I can if I must.
How about this for a where clause
... where (select count(*) from dbo.phone ph where :personid = ph.personid and ph.isprimary=1) > 0 and (select count(*) from dbo.email e where :personid = e.personid and e.isprimary=1) > 0
I can also think of a solution creating a relationships in the database configuration application, but the above query is more straight forward.