Filtering SQL rows based on values from other rows - sql

Using a SQL Server 2016 SP1 database I have this data in table name AgentsTable:
SELECT * FROM AgentsTable;
user_id first_name last_name agent_id agent_status agent_code
2003015038088 John Brown 22307 Retired 12345
2003015038088 John Brown 22307 Death 12399
4432442556456 Mary Jane 24667 Active 32133
7746234737464 Harry Smith 29981 Retired 42354
3455555657677 Mark Aguy 29654 Active 34655
5436546674465 Sally Sam 22584 Retired 45464
The second row shows that 'John Brown' is dead (agent_status = 'Death'), so I would like to exclude all rows for that agent_id who is dead. (Note that John Brown has two different agent_codes, so there's one row for each agent_code. That's how the source data is...)
This query:
SELECT * FROM AgentsTable WHERE agent_status = 'Retired';
Will return this:
user_id first_name last_name agent_id agent_status agent_code
2003015038088 John Brown 22307 Retired 12345
7746234737464 Harry Smith 29981 Retired 42354
5436546674465 Sally Sam 22584 Retired 45464
The result I want, where John Brown (22307) is excluded, is:
user_id first_name last_name agent_id agent_status agent_code
7746234737464 Harry Smith 29981 Retired 42354
5436546674465 Sally Sam 22584 Retired 45464
How can I achieve that? In other words, how can I exclude one row based on a value in another related row?

You can use CTE and get agents who are not deceased as given below:
;WITH CTE_deceasedagents AS
(
SELECT agent_id
FROM AgentsTable
WHERE agent_status = 'Death'
)
SELECT *
FROM AgentsTable as a
WHERE NOT EXISTS
(
SELECT agent_id
FROM CTE_deceasedagents as c
WHERE c.agent_id = a.agent_id
);
GO

SELECT * FROM AgentsTable t1 WHERE agent_status = 'Retired' where NOT EXISTS (SELECT 1 FROM AgentsTable t2 WHERE t2.agent_status = 'Death' and t2.agent_code = t1. agent_code );

I would just use not exists with a correlated subquery to ensure that no other record exists with the same user_id and a 'Death' status:
select a.*
from agentsTable a
where not exists (
select 1
from agentsTable a1
where a1.user_id = a.user_id and a1.agent_status = 'Death'
)

Related

Changing record values based on whether there are duplicates when two tables are combined

I know I can join Table #1 and Table #2 with a UNION and then filter out duplicate Id's using DISTINCT. However, for the duplicate contacts I'd like to change DrinkPreference to Coke/Pepsi.
Is this possible?
Starting Table #1
Id
FirstName
LastName
DrinkPreference
123
Tom
Bannon
Pepsi
124
Sarah
Smith
Pepsi
Starting Table #2
id
FirstName
LastName
DrinkPreference
125
Jim
Henry
Coke
123
Tom
Bannon
Coke
Table? #3 - combined with DrinkPreference set to Coke/Pepsi where contact exists in both tables?
Id
FirstName
LastName
DrinkPreference
125
Jim
Henry
Coke
123
Tom
Bannon
Coke/Pepsi
124
Sarah
Smith
Pepsi
You can try this one
SELECT coalesce(t1.firstname, t2.firstname) AS firstname,coalesce(t1.lastname,t2.lastname) AS lastname, CASE WHEN t1.drinkpreferences IS NULL THEN t2.drinkpreferences WHEN t2.drinkpreferences IS NULL THEN t1.drinkpreferences
ELSE t1.drinkpreferences || '/' || t2.drinkpreferences END AS drinkpreferences FROM table1 t1 FULL JOIN table2 t2 ON t1.id = t2.id
Achievable using multiple unions and joins.
select distinct FirstName, LastName, case when ct = 2 then 'Coke/Pepsi' else DrinkPreference end
from (
select FirstName, LastName, DrinkPreference, Id from table1
union all
select FirstName, LastName, DrinkPreference, Id from table2) a
left join
(
select count(1)ct, Id from
(select Id from table1
union all
select Id from table2) t1
group by Id
) b on b.Id = a.Id

Turning rows to columns in postgres

I have one table with rental contracts. (Postgres v10.18)
Like this:
Table Rental
Id Start Main_tenant_id Obect_id
101011 1.1.2021 1000 200
100222 1.1.2021 2050 210
If the Object has more than one Tenant the other ones a saved in a separate Table like this:
Table Rental_extra
Id rental_id xtra_tenant
20001 100222 3000
20002 100222 2700
20003 100222 2800
And i have a Person table like this:
Table Person
Id first_name last_name
1000 Peter Mcdonald
2050 Dan Hunt
3000 Steve Cole
2700 Ian Wright
2800 William Pears
Now i need to get this output:
Goal
Id tenant 1 tenant 2 tenant 3 tenant 4
101011 Peter Mcdonald null null null
100222 Dan Hunt Steve Cole Ian Wright William Pears
What's the best way? I tried with some crosstab example but couldn't make it work.
SELECT *
FROM crosstab(
$$
SELECT t.id, tenant_nr, concat_ws(' ', p.first_name, p.last_name)
FROM (
SELECT id, 0 AS tenant_nr, main_tenant_id AS tenant_id
FROM rental
UNION ALL
SELECT rental_id, row_number() OVER (PARTITION BY rental_id ORDER BY id), xtra_tenant
FROM extra_tenant
) t
JOIN person p ON p.id = t.tenant_id
ORDER BY 1 DESC, t.tenant_nr
$$
) AS goal(id int, tenant_1 text, tenant_2 text, tenant_3 text, tenant_4 text);
db<>fiddle here
Detailed explanation here:
PostgreSQL Crosstab Query
Postgres - Transpose Rows to Columns

How to return a count of duplicates and unique values into a column in Access

I currently have this table:
First_Name
Last_Name
Jane
Doe
John
Smith
Bob
Smith
Alice
Smith
And I'm looking to get the table to look for duplicates in the last name and return a value into a new column and exclude any null/unique values like the table below, or return a Yes/No into the third column.
First_Name
Last_Name
Duplicates
Jane
Doe
0
John
Smith
3
Bob
Smith
3
Alice
Smith
3
OR
First_Name
Last_Name
Duplicates
Jane
Doe
No
John
Smith
Yes
Bob
Smith
Yes
Alice
Smith
Yes
When I'm trying to enter the query into the Access Database, I keep getting the run-time 3141 error.
The code that I tried in order to get the first option is:
SELECT first_name, last_name, COUNT (last_name) AS Duplicates
FROM table
GROUP BY last_name, first_name
HAVING COUNT(last_name)=>0
You can use a subquery. But I would recommend 1 instead of 0:
select t.*,
(select count(*)
from t as t2
where t2.last_name = t.last_name
)
from t;
If you really want zero instead of 1, then one method is:
select t.*,
(select iif(count(*) = 1, 0, count(*))
from t as t2
where t2.last_name = t.last_name
)
from t;

select rows from a database table with common field in sqlite

i have a table as follows:
create table table1(id integer,firstname text,lastname text);
firstname lastname======== =========
1 ben taylor
2 rob taylor
3 rob smith
4 rob zombie
5 peter smith
6 ben smith
7 peter taylor
I want to select rows with a lastname , where the lastname must be shared by ben and rob and firstnames must be ben and rob.
Hence in the above table the result of the select query must be:
1 ben taylor
2 rob taylor
3 rob smith
6 ben smith
what must be the sql query to get the above results?
I tried - select * from table1 as a,table1 as b where a.firstname='ben' and b.firstname='rob' and a.lastname=b.lastname this joined all the resultant rows which is not what i inteneded.
You can use two filtering joins to demand that the lastname is shared with both a Ben and a Rob:
select *
from Table1 t1
join Table1 rob
on rob.firstname = 'rob'
and t1.lastname = rob.lastname
join Table1 ben
on ben.firstname = 'ben'
and t1.lastname = ben.lastname
where t1.firstname in ('ben', 'rob')
Live example at SQL Fiddle.
select *
from table1 where first_name = 'ben' or first_name = 'rob'
and last_name
in (select last_name from table1 where first_name = 'rob' and last_name
in (select last_name from table1 where first_name = 'ben'))

Select query which returns exect no of rows as compare in values of sub query

I have got a table named student. I have written this query:
select * From student where sname in ('rajesh','rohit','rajesh')
In the above query it's returning me two records; one matching 'rajesh' and another matching: 'rohit'.
But i want there to be 3 records: 2 for 'rajesh' and 1 for 'rohit'.
Please provide me some solution or tell me where i am missing.
NOTE: the count of result of sub query is not fix there can be many words there some distinct and some multiple occurrence .
Thanks
Your requirements are not clear, and I'll try to explain why.
Let's define table students
ID FirstName LastName
1 John Smith
2 Mike Smith
3 Ben Bray
4 John Bray
5 John Smith
6 Bill Lynch
7 Bill Smith
Query with WHERE clause:
FirstName in ('Mike', 'Ben', 'Mike')
will return 2 rows only, because it could be rewritten as:
FirstName='Mike' or FirstName='Ben' or FirstName='Mike'
WHERE is filtering clause that just says if existing row satisfy given conditions or not (for each of rows created by FROM clause.
Let's say we have subquery that returns any number of non distinct FirstNames
In case if SQ contains 'Mike', 'Ben', 'Mike' using inner join you can get those 3 rows without problem
Select ST.* from Students ST
Inner Join (Select name from …. <your subquery>) SQ
On ST.FirstName=SQ.name
Result will be:
ID FirstName LastName
2 Mike Smith
2 Mike Smith
3 Ben Bray
Note data are not ordered by order of names returning by SQ. If you want that, SQ should return some ordering number, eg.:
Ord Name
1. Mike
2. Ben
3. Mike
In that case query should be:
Select ST.* from Students ST
Inner Join (Select ord, name from …. <your subquery>) SQ
On ST.FirstName=SQ.name
Order By SQ.ord
And result:
ID FirstName LastName
2 Mike Smith (1)
3 Ben Bray (2)
2 Mike Smith (3)
Now, let's se what will happen if subquery returns
Ord Name
1. Mike
2. Bill
3. Mike
You will end up with
ID FirstName LastName
2 Mike Smith (1)
6 Bill Lynch (2)
7 Bill Smith (2)
2 Mike Smith (3)
Even worse, if you have something like:
Ord Name
1. John
2. Bill
3. John
Result is:
ID FirstName LastName
1 John Smith (1)
4 John Bray (1)
5 John Smith (1)
6 Bill Lynch (2)
7 Bill Smith (2)
1 John Smith (3)
4 John Bray (3)
5 John Smith (3)
This is an complex situation, and you have to clarify precisely what requirement is.
If you need only one student with the same name, for each of rows in SQ, you can use something like SQL 2005+):
;With st1 as
(
Select Row_Number() over (Partition by SQ.ord Order By ID) as rowNum,
ST.ID,
ST.FirstName,
ST.LastName,
SQ.ord
from Students ST
Inner Join (Select ord, name from …. <your subquery>) SQ
On ST.FirstName=SQ.name
)
Select ID, FirstName, LastName
From st1
Where rowNum=1 -- that was missing row, added later
Order By ord
It will return (for SQ values John, Bill, John)
ID FirstName LastName
1 John Smith (1)
6 Bill Lynch (2)
1 John Smith (3)
Note, numbers (1),(2),(3) are shown to display value of ord although they are not returned by query.
If you can split the where clause in your calling code, you could perform a UNION ALL on each clause.
SELECT * FROM Student WHERE sname = 'rajesh'
UNION ALL SELECT * FROM Student WHERE sname = 'rohit'
UNION ALL SELECT * FROM Student WHERE sname = 'rajesh'
Try using a JOIN:
SELECT ...
FROM Student s
INNER JOIN (
SELECT 'rajesh' AS sname
UNION ALL
SELECT 'rohit'
UNION ALL
SELECT 'rajesh') t ON s.sname = t.sname
just because you've got a criteria in there two times doesn't mean that it will return 1 result per criteria. SQL engines usually just use the unique criteria - thus, from your example, there will be 2 criteria in IN clause: 'rajesh','rohit'
WHY do you need to return 2 results? are there two rajesh in your table? they should BOTH return then. You don't need to ask for rajesh twice for that to happen. What does your data look like? What do you want to see returned?
Hi i am query just as you give above and it give me all data that matches in the condition of in clause. just like your post
select * from person
where personid in (
'Carson','Kim','Carson'
)
order by FirstName
and its give me all records which fulfill this Criteria