SQL help to make a particular select statement for a table - sql

CREATE TABLE person (
id INTEGER PRIMARY KEY,
first_name TEXT,
last_name TEXT,
age INTEGER
);
INSERT INTO person VALUES (0, 'Pat', 'Johnson', 25);
INSERT INTO person VALUES (1, 'John', 'Wells', 28);
So I have a table called person and I am trying to query all people younger than Pat Johnson, but I do not want to query it by hard coding.
SELECT first_name, last_name FROM person WHERE age < 25;
^^^ this is an example of what I do not want to do.

You can try using a subquery
SELECT first_name, last_name FROM person
WHERE age<(select age from person where first_name='Pat' and last_name='Johnson')

We can try an anti-join approach:
SELECT t1.first_name, t1.last_name
FROM yourTable t1
LEFT JOIN yourTable t2
ON t1.age > t2.age AND
t2.first_name = 'Pat' AND
t2.last_name = 'Johnson'
WHERE
t2.first_name IS NULL;

Related

Update column value when their other column values are same

The Problem is, StudentId in row number 2 should be 1 because their Name and Father Name are the same. Similarly, StudentId in row number 4 should be 3 because Name and Father Name are the same
I have revised my query according to my requirement as I have to update my student_id when student_name, student dob, student father, email, countryId, and skype id will same now updated query will be
Merge into student_new_Backup
using ( select min(student_id) as stid, student_name, student_father,student_dob,email_id,skype_id,country
from student_new_Backup
group by student_name, student_father,student_dob,email_id,skype_id,country) a
on (student_new_Backup.student_name= a.student_name
and student_new_Backup.student_father=a.student_father
and student_new_Backup.student_dob=a.student_dob
unfortunately no student_id is update
and student_new_Backup.email_id=a.email_id
and student_new_Backup.skype_id=a.skype_id
and student_new_Backup.country=a.country)
when matched then
UPDATE SET student_new_Backup.student_id=a.stid
;
commit;
unfortunately, no student_id is an update
I think you want:
update t
set studentId = (select min(t2.studentId)
from t t2
where t2.name = t.name and t2.fathername = t.fathername
);
EDIT:
If you want NULL safe comparisons, then you need be be explicit:
update t
set studentId = (select min(t2.studentId)
from t t2
where (t2.name = t.name or t2.name is null and t.name is null) and
(t2.fathername = t.fathername or t2.fathername is null and t.fathername is null)
);
you can use merge for update
Merge into table_name
using ( select min(studentId) as stid, Name, fathername
from table_name
group by Name, fathername) a
on (table_name.Name= a.Name
and table_name.fatherName=a.fatherName)
when matched then
UPDATE SET table_name.studentId=a.stid
online demo

how to get employee name using corresponding id from lookup table using sql

I have a sql query as following:
table1 has:
name1, name2, date .....
user_table has:
employee_id, employee_name
table1 has id values under column name1 and name2 and user_table has id and corresponding name.
It would have been a straight forward join. But after the date 2019-03-20, name1 and name2 from table1 have id like 100101, 100102. And before 2019-03-20, name1 and name2 has values such as tom, dick, harry etc.
The goal here is obvious, to replace the id values with employee names in table1
My initial idea is to do a UNION between two segments of the table1, before 2019-03-20 and after 2019-03-20.
select t.*, u.employee_name as name1a, u1.employee_name as name2a
from table1 t
left join user_table u on t.name1= u.employee_id
left join user_table u1 on t.name2 = u1.employee_id
where
cast(t.approvedate as date) > '2019-03-20';
question 1: Is there a better solution than doing UNION?
question 2: To do a UNION both sides must have the same number of columns. But the query above will produce two additional columns name1a, name2a. Now I can just select the column names to avoid that issue, but what if I have too many columns to list in the select statement ?
Updated with sample table and desired result. I have following tables:
test_sales
CREATE TABLE test_sales (product varchar(20) ,sales_date varchar(20), person_1 varchar(20) , person_2 varchar(20) ) ;
INSERT INTO test_sales (product, sales_date, person_1, person_2) VALUES ('abc', '2019-04-01', '101', '110'), ('abc', '2019-04-10', '102', '111'),('abc', '2019-03-15', 'tom', 'john'), ('xyz', '2019-03-21', 'tom', 'dick'), ('xyz', '2019-03-29', 'harry', 'josh'), ('xyz', '2019-04-05', '102', '110'), ('xyz', '2019-03-29', 'harry', 'josh'), ('pqr', '2019-04-02', '101', '111');
test_user
CREATE TABLE test_user (employee_id varchar(10) ,employee_name varchar(20));
INSERT INTO test_user (employee_id, employee_name) VALUES ('101', 'john'),('102', 'josh'), ('110', 'tom'), ('111', 'dick');
And I want to get following output where the blank cells will also have names.
Right now I have this query which produces the result with blank cells.
select s.product, s.sales_date, u.employee_name as person_1, u1.employee_name as person_2 from test_sales s left join test_user u on s.person_1 = u.employee_id left join test_user u1 on s.person_2 =u1.employee_id;
You could join using an OR operator:
select t.*, u.employee_name as name1a, u1.employee_name as name2a
from table1 t
left join user_table u
on t.name1= u.employee_id
OR t.name2 = u.employee_id
OR t.name1 = u.employee_name
OR t.name2 = u.employee_name
It's not going to be fast, but it may do the trick. Optionally you can do 4 joins (name1 and name2 for id, and then name1 and name2 for name) and use coalesce:
select t.*, COALESCE(u.employee_name, u2.employee_name, u3.employee_name, u4.employee_name) AS employee_name
from table1 t
left join user_table u
on t.name1= u.employee_id
left join user_table u2
ON t.name2 = u2.employee_id
left join user_table u3
ON t.name1 = u3.employee_name
left join user_table u4
ON t.name2 = u4.employee_name

Retrieving the Name from the same row using a recently acquired ID

I was messing around and wanted to know (title). Let me explain.
Let's say I have an Employee table and in that table is ID ManagerID and Name ID. So after making the Query to find everyone's manager you are left with something like this as a result
Person's Name | Their ID | Manager ID
Now he's what I want to know if it is possible. The managers are also in this employee table and they either have other employees as their manager or they dont have a manager. So technically I have all the information I need at my disposal. Just need to exchange the int (ManagerID) with the Person's name that is in the same row.
Is that possible or is SQL not able to handle that kind of swapping.
Edit:
So here's the test table and values I've been using:
Create table Employee
( ID int,
Name varchar (50),
ManagerID int );
Insert into Employee
Values( 101, 'Joe Smoeington', 102);
Insert into Employee
Values( 102, 'This Guy', 106);
Insert into Employee
Values(103, 'Test', 102);
Insert into Employee
Values(104, 'Rob', 105);
Insert into Employee
Values(105 , 'Jeremy', NULL);
Insert into Employee
Values(106 , 'Sam', NULL);
My stuck point:
Select ID, Name, ManagerID
From Employee
Where ManagerID IS NOT NULL
Ultimately this will get you a table that looks like this:
ID Name ManagerID
101 Joe Smoeington 102
102 This Guy 106
103 Test 102
104 Rob 105
Things Ive tried:
Select ID, Name, ManagerID
From Employee Join Employee
On Employee.ID = Employee.ManagerID
Where ManagerID IS NOT NULL
Select ID, Name, (Select Name From Employee Where (didnt know what to put here and gave up on this attempt)
From Employee
Where ManagerID IS NOT NULL
There are others just not worth mentioning
You can use self join something like this.
SELECT T1.Name AS Employee_Name,T1.ID AS Employee_ID,(IFNULL(T2.ID,'NA')) AS ManagerID
FROM
employee T1
LEFT JOIN employee T2 ON T1.ManagerID = T2.ID
EDIT :
IF you want to see only those employee who has manager.
SELECT T1.Name AS Employee_Name,T1.ID AS Employee_ID,(IFNULL(T2.ID,'NA')) AS ManagerID
FROM
employee T1
INNER JOIN employee T2 ON T1.ManagerID = T2.ID
Here is the DEMO
hope This Helps.

inner join and partial matches

I'm not sure if the title's correct, so here goes.
The following script returns the correct result: currently Roy Brown teaches Math 101. Roy Brown's TeacherID is 1225, but then they later added the prefix 001- for other purposes.
What I would like to include in the result is some identification that Roy Brown did have another course at one point in time, since Math 101 Old has 1225. It doesn't even have to show how many other courses he had; just something that will let me know that there is more than one row in #coursesCsv. But the result should remain to 2 rows.
What I don't want is to display an extra row for Roy Brown, which is why I'm not doing the commented inner join (ie. right(t.teacherid,4) = right(c.teacherid,4)).
There are no relationships between these two tables since the data in #coursesCSV comes from a csv file.
IF OBJECT_ID('tempdb..#teacher') IS NOT NULL DROP TABLE #teacher
IF OBJECT_ID('tempdb..#coursesCsv') IS NOT NULL DROP TABLE #coursesCsv
create table #teacher
(
TeacherID varchar(10),
FullName varchar(30)
)
insert into #teacher select '001-1225', 'Roy Brown'
insert into #teacher select '001-1230', 'Woody Boyd'
create table #coursesCsv
(
CourseName varchar(30),
TeacherID varchar(10)
)
insert into #coursesCsv select 'Math 101', '001-1225'
insert into #coursesCsv select 'Math 101 Old', '002-1225'
insert into #coursesCsv select 'History 101', '001-1230'
select t.teacherid, c.coursename from
#teacher t inner join #coursesCsv c
on t.teacherid = c.teacherid
--on right(t.teacherid,4) = right(c.teacherid,4)
Do a "Group Count" of courses for the same right(teacherid,4) within a Derived Table (or a Common Table Expression = WITH) and join to it:
select t.teacherid, c.coursename, c.coursecnt
from teacher t
inner join
(
select
teacherid,
coursename,
count(*)
over (partition by right(teacherid,4)) as coursecnt
from coursesCsv
) as c
on t.teacherid = c.teacherid

SQL Query JOIN or IN operator?

I have two tables,
PERSON
and FRIENDS.
FRIENDS has the fields NAME and SURNAME.
A person has N friends.
I want to retrieve all the PERSONs that have atleast two FRIENDs, one with name ="mark", and the other with name="rocco" and surname ="siffredi".
Example: if I have a person that has 5 friends, one of them is called mark and no one is called rocco siffredi, no tables are returned.
I was thinking about:
SELECT * FROM person p
JOIN friends AS f ON p.ID=f.personID
WHERE f.name ="mark" AND f IN
( SELECT * from FRIENDS WHERE name="rocco" and surname="siffredi")
or
SELECT * FROM person p
JOIN friends AS f1 ON p.ID=f1.personID
JOIN friends AS f2 ON p.ID=f2.personID
WHERE f1.name="mark" AND f2.name="rocco" AND f2.surname="siffredi"
What is the best way? I mean the fastest way to execute it.
I don't care about readability.
Is there any other way to execute this query?
Ty.
EDIT: added the join on the ID...
I had to guess your column names and make up a table:
Use EXISTS:
CREATE table FRIENDS(person_id INT, friend_id INT)
go
SELECT *
FROM person
WHERE
EXISTS
(SELECT *
FROM friends f
JOIN person per
ON f.friend_id = per.id
WHERE
per.name ='mark' AND
person.id = f.person_id) AND
EXISTS
(SELECT *
FROM friends f
JOIN person per
ON f.friend_id = per.id
WHERE
per.name = 'rocco' AND
per.surname='siffredi' AND
person.id = f.person_id)
Your schema design isn't very good for what you are trying to do... I would have a Person table as you have, which would also contain a unique identifier called PersonId. I would then have a Friends table which took two fields - Person1Id and Person2Id.
This gives you a couple of important advantages - first of all your system is able to handle more than one bloke called John Smith (because we join on Ids rather than Names...). Secondly, a person's details are only ever recorded in the Person table. One definition of truth...
With these data as input:
INSERT INTO Person VALUES
(1, 'Bob', 'Smith'),
(2, 'Jim', 'Jones')
INSERT INTO Friends VALUES
(1, 1, 'Mark', 'Tally'),
(2, 1, 'John', 'Smith'),
(3, 1, 'Jack', 'Pollock'),
(4, 2, 'Mark', 'Rush'),
(5, 2, 'Rocco', 'Siffredi'),
(6, 2, 'Mark', 'Bush')
you can use this query:
SELECT PersonId, COUNT(*) AS NoOfFriends
FROM (
SELECT DISTINCT PersonId, Name,
Surname = CASE WHEN NAME = 'Mark' THEN NULl
ELSE Surname
END
FROM Friends
WHERE Name = 'Mark' OR (Name = 'Rocco' AND Surname = 'Siffredi') ) t
GROUP BY PersonId
to get the distinct number of required friends per PersonID:
PersonId NoOfFriends
------------------------
1 1
2 2
You can now join with the above table expression on PersonId and filter it by NoOfFriends:
SELECT p.*
FROM Person AS p
INNER JOIN (
SELECT PersonId, COUNT(*) AS NoOfFriends
FROM (
SELECT DISTINCT PersonId, Name,
Surname = CASE WHEN NAME = 'Mark' THEN NULl
ELSE Surname
END
FROM Friends
WHERE Name = 'Mark' OR (Name = 'Rocco' AND Surname = 'Siffredi') ) t
GROUP BY PersonId ) s ON s.PersonId = p.ID
WHERE s.NoOfFriends = 2
so as to get only persons having the required combination of associated friends:
ID Name Surname
-------------------
2 Jim Jones
P.S. I have completely re-written my answer after #t-clausen.dk's comment.