Using a parameter from another query in a like clause - sql

I am trying to do a join and where column using an inner query. the actual query is a bit more complicated that the below but I have simplified it for this questiion. Consider I have a peopel table. I have in it two likes. The first name is George in both rows but the last_name is Lloyd and Lloyds in each row. I want to be able to return both rows. I am looking for Lloyd - returning that in the inner join and then trying to do a like int he outside query. This only returns the first row though with Lloyd. not the one with the s.
Is it possible to do this? If I was looking for Lloyd and hardcoding it I could do
like 'Lloyd%'
but in my case there are many rows Im returning so I want to do it dynamically.
select * from people p
join
(select p1.id, p1.first_name, p1.last_name
from people p1
where p1.first_name = 'George' and p1.last_name = 'Lloyd' and p1.id = 17)
un on un.id = p.id
where p.first_name = un.first_name and p.last_name like '%' || un.last_name || '%'
Thanks

You are only receiving one row because your query is predicated on just one row (assuming id is unique in your table)
Remove the id = 17
select *
from people
where first_name = :first_name
and last_name like :last_name

You can try this:
SELECT * FROM PEOPLE P
INNER JOIN PEOPLE P2 ON P.FIRST_NAME = P2.FIRST_NAME
and instr(upper(p.last_name),upper(p2.last_name))<>0;

Because your join condition is on un.id = p.id, your result set is always restricted to id = 17.
It sounds like this is the query you were trying to write:
select * from people p
join
(select p1.id, p1.first_name, p1.last_name
from people p1
where p1.first_name = 'George' and p1.last_name = 'Lloyd' and p1.id = 17)
un on p.first_name = un.first_name and p.last_name like '%' || un.last_name || '%'
... which I would personally clean up a little to this:
select p2.*
from people p1
join people p2
on p2.first_name = p1.first_name
and p2.last_name like '%' || p1.last_name || '%'
where p1.first_name = 'George'
and p1.last_name = 'Lloyd'
and p1.id = 17

Related

How can I join 3 tables, limit the query based on a condition, and return empty columns if condition not met?

I have the following query currently:
select * from people
LEFT JOIN addresses
ON people.id = addresses.id
LEFT JOIN pers
ON people.id = pers.pers_id
WHERE people.id =:id
AND addresses.is_primary = 'Y'
Of course if there is no address where is_primary = 'Y' for a given person, the query doesn't return any results.
Without is_primary='Y', the query returns multiple addresses.
Is there any way, instead, to return null columns for all of the address fields in the event where there is no record for the id where is_primary = 'Y'?
You can do something like this -
select *
from people
LEFT JOIN addresses
ON people.pidm = addresses.pidm
and addresses.is_primary = 'Y'
RIGHT JOIN pers
ON people.id = pers.pers_id
WHERE people.id = :id
use case when
select *,case when addresses.is_primary not in('Y') then 'primary address different' else addresses.is_primary end as is_primary from people
LEFT JOIN addresses
ON people.pidm = addresses.pidm
RIGHT JOIN pers
ON people.id = pers.pers_id
WHERE people.id =:id
I strongly recommend that you not mix left join and right join. The query is just so hard to follow.
Instead, start with the table where you want to keep all the rows. Then only use left join. Sometimes, you may need to put conditions in the on clause.
In your case:
select *
from people p left join
addresses a
on p.pidm = a.pidm and a.is_primary = 'Y' left join
pers
on p.id = pers.pers_id
where p.id = :id;

HSQLDB v.2.3.0: USING predicate working as intended?

The following query when executed against snapshot 50 of HSQLDB 2.3.0 produces an error. The error message is "Error: duplicate column name in derived table: INST_ID"
SELECT c.lastname, to_date_string(c.dob), i.itag|| ': ' ||
m.mrn, to_date_string(en.dofs),
to_date_string(en.dols), pa.payertag,
y.dxname, d.icd9, d.icd10, d.icd10name,
to_datetime_string(s.dos), r.cpt, r.rxname, p.lastname || ', ' ||
p.firstname
FROM Encounters AS en
INNER JOIN Clients AS c USING ( cli_id )
INNER JOIN Client_MRNs AS m ON c.defmrn_id = m.mrn_id
INNER JOIN Institutions AS i USING ( inst_id )
INNER JOIN Payers AS pa USING ( payer_id )
INNER JOIN Encounter_DXs AS x USING ( enc_id )
INNER JOIN Diagnoses AS d USING ( dx_id )
INNER JOIN DXSynonyms AS y ON d.defsyn_id = y.syn_id
INNER JOIN Services AS s USING ( enc_id )
INNER JOIN RXCodes AS r USING ( rx_id )
INNER JOIN Providers AS p USING ( prov_id )
WHERE (s.dos >= 56453 AND s.dos < 56461)
ORDER BY c.lastname, en.dofs, s.dos;
However, when I execute the same query but replace all the USING predicates with ON ... = phrases it executes successfully:
SELECT c.lastname, to_date_string(c.dob), i.itag|| ': ' ||
m.mrn, to_date_string(en.dofs),
to_date_string(en.dols), pa.payertag,
y.dxname, d.icd9, d.icd10, d.icd10name,
to_datetime_string(s.dos), r.cpt, r.rxname, p.lastname || ', ' ||
p.firstname
FROM Encounters AS en
INNER JOIN Clients AS c ON c.cli_id = en.cli_id
INNER JOIN Client_MRNs AS m ON c.defmrn_id = m.mrn_id
INNER JOIN Institutions AS i ON i.inst_id = m.inst_id
INNER JOIN Payers AS pa ON pa.payer_id = en.payer_id
INNER JOIN Encounter_DXs AS x ON x.enc_id = en.enc_id
INNER JOIN Diagnoses AS d ON d.dx_id = x.dx_id
INNER JOIN DXSynonyms AS y ON d.defsyn_id = y.syn_id
INNER JOIN Services AS s ON s.enc_id = en.enc_id
INNER JOIN RXCodes AS r ON r.rx_id = s.rx_id
INNER JOIN Providers AS p ON p.prov_id = s.prov_id
WHERE (s.dos >= 56453 AND s.dos < 56461)
ORDER BY c.lastname, en.dofs, s.dos;
Is this working as intended? I like using USING because it results in less verbose, cleaner code. I won't include the DDL for the tables right now (but can), because the queries are big and involve many tables, but there are three tables that have INST_ID fields. One table has it as the primary key, and the other two have foreign keys to it. Really the only difference in the queries is "ON" vs "USING".
After the first join, there is one INST_ID from ENCOUNTERS. After the second join, there is an additional one from CLIENTS_MRNS. The third join fails because of this duplication.

Joining a table to itself to compare two different types of fields

sorry for the confusing title.
I have two tables, individu and orgcont. individu looks something like:
individu
-------------
ind_id
org_id
email
orgcont looks something like:
orgcont
-------------
ind_id
function_code
I have many users that can belong to the same org_id and they each of their own ind_id. The 'leaders' of the org_id have a function code of 'PRIM', everyone else's are irrelevant. I am trying to return a list of org_id and email of all of the users who have a 'PRIM' function code who have users that have NULL email addresses that are not 'PRIM'.
I have something like this, but the results are not correct. Any help would be appreciated:
select
i.email,
i.org_id
from
individu i,
orgcont o,
individu i2,
orgcont o2
where
i.ind_id = o.ind_id
and i.org_id = i2.org_id
and i2.ind_id = o2.ind_id
and o.function_code = 'PRIM'
and o2.function_code != 'PRIM'
and i2.email is NULL
Try it with exists:
select
i.email,
i.org_id
from
individu i
inner join orgcont o on
i.ind_id = o.ind_id
where
o.function_code = 'PRIM'
and exists (
select
1
from
individu i2
inner join orgcont o2 on
i2.ind_id = o2.ind_id
where
i2.org_id = i.org_id
and o2.function_code != 'PRIM'
and i2.email is null
)
This will only return you one row for each leader with folks in their org with null email addresses, as opposed to your original query, which will return duplicate rows.
Also note the join syntax--you want to do an inner join here, not a cross join, so do so explicitly.
Additionally, you could have just done a select distinct, but that's a little more expensive than the exists.
This should work, the INNER JOIN to the sub query will act as the filter
SELECT DISTINCT i.email, i.org_id
FROM individu i
INNER JOIN orgcont o ON i.ind_id = o.ind_id
INNER JOIN
( SELECT i.id
FROM individu i
INNER JOIN orgcont o ON i.ind_id = o.ind_id
WHERE o.function_code != 'PRIM' AND i.email IS NULL
) t ON t.id = i.id
WHERE o.function_code = 'PRIM'

Finding unique matches in 2 separate databases

I have 2 databases that have the same structure, but different data. Both are SQL 2005.
I am trying to find which of the Persons in Database A, exist in Database B. My best opportunity for match is to match on FirstName and LastName.
I only want to bring back a list of:
DatabaseA.Person
DatabaseB.Person
Where:
1. I want all records from DatabaseA, even if there is not a match in Database B.
2. I only want records from DatabaseB where the FirstName/LastName match only one record in DatabaseB.
I have written a query, where I group by, but since I need to see more data than FirstName and LastName, I cannot bring it back without grouping it - which gives me many duplicates. What kind of query should I be using? Do I need to use a cursor?
Here is my query now, which sort of works - except I'm getting results for duplicates in DatabaseB and all I want to know about Database B is when FirstName/LastName matches to one distinct record and no others. My objective is to get a list of people that I know are the same person in 2 databases so that I can build a dictionary list of department code mappings between employees.
select
count(DatabaseAEmployee.id) as matchcount
, DatabaseAPerson.id as DatabaseAPersonid
, DatabaseAEmployee.DeptCode DatabaseADeptCode
, DatabaseAPerson.firstname as DatabaseAfirst
, DatabaseAPerson.lastname as DatabaseAlast
, DatabaseBPerson.id as DatabaseBPersonid
, DatabaseBEmployee.DeptCode as DatabaseBDeptCode
, DatabaseBPerson.firstname as DatabaseBfirst
, DatabaseBPerson.lastname as DatabaseBlast
, DatabaseAPerson.ssn as DatabaseAssn
, DatabaseBPerson.ssn as DatabaseBssn
, DatabaseAPerson.dateofbirth as DatabaseAdob
, DatabaseBPerson.dateofbirth as DatabaseBdob
FROM [DatabaseA].[dbo].Employee DatabaseAEmployee
LEFT OUTER JOIN [DatabaseA].[dbo].Person DatabaseAPerson
ON DatabaseAPerson.id = DatabaseAEmployee.id
LEFT OUTER JOIN [DatabaseB].[dbo].Person DatabaseBPerson
ON
DatabaseAPerson.firstname = DatabaseBPerson.firstname
AND
DatabaseAPerson.lastname = DatabaseBPerson.lastname
LEFT OUTER JOIN [DatabaseB].[dbo].Employee DatabaseBEmployee
on DatabaseBEmployee.id = DatabaseBPerson.id
group by
DatabaseAPerson.firstname
, DatabaseAPerson.lastname
, DatabaseAPerson.id
, DatabaseAEmployee.DeptCode
, DatabaseBPerson.id
, DatabaseBEmployee.DeptCode
, DatabaseBPerson.firstname
, DatabaseBPerson.lastname
, DatabaseBPerson.ssn
, DatabaseAPerson.ssn
, DatabaseBPerson.dateofbirth
, DatabaseAPerson.dateofbirth
Here's what I'm trying now, but I'm getting duplicates on the left side:
with UniqueMatchedPersons (Id, FirstName, LastName)
as (
select
p2.ID, p2.FirstName, p2.LastName
from
[DatabaseA].[dbo].[Employee] p1
INNER JOIN [DatabaseA].[dbo].[Person] p2 on p1.id = p2.id
inner join [DatabaseB].[dbo].[Person] p3
on p2.FirstName = p3.FirstName and p2.LastName = p3.LastName
INNER JOIN [DatabaseB].[dbo].[Employee] p4
on p3.id = p4.id
group by p2.ID, p2.FirstName, p2.LastName
having count(p2.ID) = 1
)
select p1.*, p2.*
from DatabaseA.dbo.Person p1
inner join UniqueMatchedPersons on p1.ID = UniqueMatchedPersons.ID
left outer join DatabaseB.dbo.Person p2
on p1.FirstName = p2.FirstName and p1.LastName = p2.LastName
Try this:
SELECT id,FirstName,Lastname
FROM dba.Persons
UNION
SELECT b.id,b.FirstName,b.LastName
FROM dbb.Persons as b
INNER JOIN dba.Persons as a
ON b.FirstName = a.FirstName AND b.LastName = a.LastName
If you want to get all from A and only those from B that DON'T have a match (which would make more sense to me) i'd use this:
SELECT id,FirstName,Lastname
FROM dba.Persons
UNION
SELECT b.id,b.FirstName,b.LastName
FROM dbb.Persons as b
LEFT OUTER JOIN dba.Persons as a
ON b.FirstName = a.FirstName AND b.LastName = a.LastName
WHERE a.id is null
Try something like:
Select dta.LastName, dta.FirstName, dta.[otherColumns] dtb.LastName, dtb.FirstName
dtb.[otherColumns]
From [databaseA].[table] as dta
LEFT OUTER JOIN [databaseB].[table] as dtb
on dta.Lastname = dtb.LastName and dta.FirstName = dtb.FirstName
That should get you: 1) everyone in table A, and 2) everyone in table B who is has a Lastname/Firstname match in table A.
Works when SQL Server (at least it should)
SELECT
A.*
, B.*
FROM
DatabaseA.dbo.Person A
LEFT JOIN DatabaseB.dbo.Person B
ON A.FirstName = B.FirstName AND A.LastName = B.LastName
Edit: You mention you receive duplicates from DatabaseB where you only need the match on first and lastname. But you also request other data (then first/lastname) this is the problem. If you distinct data they you only request that data.
Using transact-sql, the following untested query should allow you to view unique matches only:
select
p1.ID, p1.FirstName, p1.LastName
from
[DatabaseA].[dbo].[Persons] p1
left outer join [DatabaseB].[dbo].[Persons] p2
on p1.FirstName = p2.FirstName and p1.LastName = p2.LastName
group by p1.ID, p1.FirstName, p2.LastName
having count(p1.ID) = 1
If using Sql Server, this can then be encapsulated within a common table expression, to which you can perform a join.
with UniqueMatchedPersons (Id, FirstName, LastName)
as (
--query in previous code snippet
)
select persons.*
from Persons
inner join UniqueMatchedPersons on Persons.ID = UniqueMatchedPersons.ID
Update:
If you wish to select fields from both tables, you can simply respecify the original join condition that evaluated name matching before; this is because duplicated matches on the left hand side of the join have been filtered out by the having aggregate condition.
Modifying the select portion of the above snippet to read the following will allow you to select fields from either side of the join:
select p1.*, p2.*
from [DatabaseA].[dbo].[Persons] p1
inner join UniqueMatchedPersons on p1.ID = UniqueMatchedPersons.ID
left outer join [DatabaseB].[dbo].[Persons] p2
on p1.FirstName = p2.FirstName and p1.LastName = p2.LastName
Update 2:
To filter out duplicates on the left hand side (which will also cause duplicates on the right) you'll have to remove the grouping on [DatabaseA].[dbo].[Persons].[ID].
When I refer to duplicates, I mean names in adjacent rows that are identical in terms of characters and padding. If you have diacritic variations of first and last names, then the results of the name comparison will be subject to the database collation (unless you explicity declare a collation on a join expression). Likewise if you have variations in spacing, padding or punctuation between names, you may have to consider a different approach than a direct equality operator for name matching.
Try the following:
with UniqueMatchedPersons (FirstName, LastName)
as (
select
p1.FirstName, p1.LastName
from
[DatabaseA].[dbo].[Person] p1
left outer join [DatabaseB].[dbo].[Person] p2
on p2.FirstName = p3.FirstName and p2.LastName = p3.LastName
group by p1.FirstName, p1.LastName
having count(p1.FirstName) = 1
)
select p1.*, p2.*, e1.*, e2.*
from [DatabaseA].[dbo].[Person] p1
inner join UniqueMatchedPersons ump
on p1.FirstName = ump.FirstName and p1.LastName = ump.LastName
left outer join [DatabaseB].[dbo].[Person] p2
on p1.FirstName = p2.FirstName and p1.LastName = p2.LastName
inner join [DatabaseA].[dbo].[Employee] e1 on p1.ID = e1.ID
inner join [DatabaseB].[dbo].[Employee] e2 on e2.ID = p2.ID
order by p1.id asc

MYSQL Multiple Select For Same Category?

I have 3 tables (scenes, categories, scenes_categories ) in a many to many relationship.
scenes ( id, title, description )
categories ( id, title )
scenes_categories ( scene_id, category_id )
I'm having problems making a query to select scenes that must match multiple categories. For example, I might want to select scenes that match category 3 AND category 5 AND category 8, but I can't figure out how to get this to work.
So far I've got something like
SELECT scenes.id, scenes.title, scenes.description
FROM scenes
LEFT JOIN scenes_categories ON scenes.id = scenes_categories.scene_id
LEFT JOIN categories ON scenes_categories.category_id = categories.id
WHERE scenes_categories.category_id = '3'
AND scenes_categories.category_id = '5'
AND scenes_categories.category_id = '8'
AND scenes.id = '1'
How can I select for records that must match all the category ID's specified?
You need to require that a row exists in your many-to-many table for that sceneId, for each categoryId you are requiring:
So try this:
SELECT s.id, s.title, s.description
FROM scenes s
WHERE s.id = '1'
And Exists (Select * From scenes_categories
Where scene_id = s.Id
And category_id = '3')
And Exists (Select * From scenes_categories
Where scene_id = s.Id
And category_id = '5')
And Exists (Select * From scenes_categories
Where scene_id = s.Id
And category_id = '8')
another option that should work is to do three inner joins instead:
SELECT s.id, s.title, s.description
FROM scenes s
Join scenes_categories c3
On c3.scene_id = s.Id
And c3.category_id ='3'
Join scenes_categories c5
On c5.scene_id = s.Id
And c5.category_id ='5'
Join scenes_categories c8
On c8.scene_id = s.Id
And c8.category_id ='8'
WHERE s.id = '1'
Charles Bretana's answer will work, but might want to check the performance of that against this to see which works better for you.
SELECT * FROM scenes
INNER JOIN (
SELECT scene_id
FROM scenes_categories
WHERE category_id IN (3,5,8)
GROUP BY scene_id
HAVING count(*) = 3
) valid ON scenes.id = valid.scene_id
Assuming your SQL is dynamic, this will probably be a bit easier to implement.