sql, getting parents and child from one table - sql

I'm trying to write a simple sql statement to select a parent and dependents from one table based on the parents hiring date. Because the hiring date field in dependents row is null, I'm only getting the parents. Can someone help?
PRIM KEY RECORD LAST FIRST HIRE DATE
12345 1 JONES MARY 1/1/2017
12345 2 JONES TIM
6789 1 SMITH CAROL 5/12/2014
23456 1 WHITAKE REGINA 5/14/2017
23456 2 WHITAKE JOE
parent has a row for each child in the table. Parent is 1 and all dependents have a 2. They share a primary key (parent's ssn). I want to select all parents who was hired between specific date range and their dependants rows. The dependent hire date column is null. So when I write the following... I'm only getting the parent rows...
SELECT PRIMARY_KEY_VALUE, RECORD_ID, LAST_NAME, FIRST_NAME, HIRE_DATE
FROM CIGNA_ELIGIBILITY
WHERE(HIRE_DATE BETWEEN '20171101' AND '20171131');

If i understand your problem correctly, that on the date range provided, you want to return records associated with it and all dependents(provided that parents/childs has same prim_key) then one way could be to use IN.
select *
from table1 t1
where t1.prim_key in
(
select t2.prim_key
from table1 t2
where t2.hire_date between '2017-01-01' AND '2017-01-30'
);
what the above query does is that from sub-query select PRIM_KEY of the date range specified and then in main query select all record associated with it.
Result:
+---+----------+--------+-------+-------+---------------------+
| | prim_key | record | last | first | hire_date |
+---+----------+--------+-------+-------+---------------------+
| 1 | 12345 | 1 | JONES | MARY | 01.01.2017 00:00:00 |
| 2 | 12345 | 2 | JONES | TIM | NULL |
+---+----------+--------+-------+-------+---------------------+
DEMO
Update:
Another option could be to use exists:
select *
from table1 t1
where exists
(
select 1
from table1 t2
where t1.prim_key = t2.prim_key
and t2.hire_date between '2017-01-01' AND '2017-01-30'
)

Related

SQL MAX: max date from multiple locations same part

what I'm looking to find is that last or max date a part number was purchased from any store. so we can have so sales or sales and just give the max date:
part
date
loc
123
8/1/2022
store 1
123
8/2/2022
store 1
123
null
store 2
123
8/3/2022
store 3
result would be:
part
date
Loc
123
8/3/2022
store 1
123
8/3/2022
store 2
123
8/3/2022
store 3
Select the max date in a subquery for every part, it would give you one Result, the highest date.
The Query should work with most rdms
SELECT DISTINCT [part], (SELECT MAX([date]) FROM Table1 WHERE part = t1.part) [Date],[loc] FROM Table1 t1
part | Date | loc
---: | :------- | :------
123 | 8/3/2022 | store 1
123 | 8/3/2022 | store 2
123 | 8/3/2022 | store 3
db<>fiddle here
I am sure there is a more efficient way to do the query but I used a subquery. this should get you the desired result
SELECT DISTINCT m.[part], ad.x AS 'date', m.[loc]
FROM [MainTable] AS 'm'
LEFT OUTER JOIN
(SELECT MAX([date]) AS 'x', [part]
FROM [MainTable]
GROUP BY [part]) AS 'ad'
WHERE m.[part] = 123 --desired value
nbk's answer
There is the cleaner query.

Reorganize multiple rows in a new table with more columns

I have a table that looks like that:
+----------+----------+----------+----------+--------------------------+
| Club | Role | Name | Lastname | Email |
+----------+----------+----------+----------+--------------------------+
| Porto | 1 | Peter | Pan | peter.pan#mail.com |
| Porto | 2 | Michelle | Obama | michelle.obama#mail.com |
| Monaco | 1 | Serena | Williams | serena.williams#mail.com |
| Monaco | 2 | David | Beckham | david.beckham#mail.com |
+----------+----------+----------+----------+--------------------------+
and i want to get a table like that:
+----------+-----------------+-----------------+---------------------------------+-----------------+-----------------+---------------------------------+
| Club | Role 1 Name | Role 1 Lastname | Role 1 Email | Role 2 Name | Role 2 Lastname | Role 2 Email |
+----------+-----------------+-----------------+---------------------------------+-----------------+-----------------+---------------------------------+
| Porto | Peter | Pan | peter.pan#mail.com | Michelle | Obama | michelle.obama#mail.com |
| Monaco | Serena | Williams | serena.williams#mail.com | David | Beckham | david.beckham#mail.com |
+----------+-----------------+-----------------+---------------------------------+-----------------+-----------------+---------------------------------+
where the persons with different roles in each club puts in the same row.
I would ideally like to find a way to do that in Excel, but i am not sure if its possible. If not, SQL code would also help a lot.
Here is what I could come up with for an excel formula. Hopefully it can push you in the right direction.
This formula is assuming that your first table exists at the range A1:E5 and the second table exists at the range G1:M3. It is also assuming that the second table's column names are just repeating without the Role number attached to the front of it (same as the first table). This formula is an array formula, so you have to make sure to do CTRL+SHIFT+ENTER when inputting it.
{=INDEX($A$2:$E$5,
MATCH(1,($G2=$A$2:$A$5)*((FLOOR((COLUMN() - COLUMN($H$1) ) / 3,1) + 1)=$B$2:$B$5),0),
MATCH(H$1,$A$1:$E$1,0))}
The first part is using the INDEX forumla which pulls data from the range suppled ($A$2:$E$5) based on the row and column numbers supplied by the following MATCH formulas.
The first MATCH is supplying the row number for when the result of the lookup array section is equal to 1. I am checking two conditions, the first is to check for the "Club" name ($G2=$A$2:$A$5) and the second is to check for which "Role" we are currently on ((FLOOR((COLUMN() - COLUMN($H$1) ) / 3,1) + 1). This is using the FLOOR function to round the result down to the whole number and dividing by the number of columns (3 in this case: Name, Lastname, and Email).
The final MATCH is pulling the column number based on the header names from both tables. If you wanted to incorporate the changing names of the roles in the column headers, you could change this part to something like this:
{=INDEX($A$2:$E$5,
MATCH(1,($G2=$A$2:$A$5)*((FLOOR((COLUMN() - COLUMN($H$1) ) / 3,1) + 1)=$B$2:$B$5),0),
MOD(COLUMN() - COLUMN($H$1),3) + 3)}
I am adding 3 to the end of the mod because of the original range that was selected for table 1. The columns that we want to pull start at location 3 in the range.
If you want to do this in Oracle Sql, there's a nice approach in analytical sql.
To convert/swap rows to columns or columns to rows, we can use Pivot or Unpivot operators.
In you example use below query to covert data as you like,
select * from
(
with all_roles as
(select 1 role from dual union all
select 2 role from dual),
ddata as
(select 1 c_role, 'porto' club, 'peter' fname,'pan' lname,'peter.pan#mail.com' email from dual union all
select 2 c_role, 'porto' club, 'Michelle' fname, 'Obama' lname,'michelle.obama#mail.com' email from dual union all
select 1 c_role, 'monaco' club, 'Serena' fname, 'Williams' lname,'serena.williams#mail.com' email from dual union all
select 2 c_role, 'monaco' club, 'David' fname, 'Beckham' lname,'david.beckham#mail.com' email from dual )
(select role, club, fname,lname, email from ddata,all_roles
where all_roles.role=ddata.c_role)) all_data
pivot (
max(fname) fname,
max(lname) lname,
max(email) email
for role in ( 1 role1, 2 role2 )
)
order by club;

Find name of employees hired on different joining date

I wrote a query to find the employess hired on same date.
this is the query
select a.name,b.name,a.joining,b.joining from [SportsStore].[dbo].[Employees] a,
[SportsStore].[dbo].[Employees] b where a.joining = b.joining and a.name>b.name
Then a question popped into my mind. How do i find those employess only who were hired on different dates? I tried something like this
select a.name,b.name,a.joining,b.joining from [SportsStore].[dbo].[Employees] a,
[SportsStore].[dbo].[Employees] b where a.joining != b.joining and a.name>b.name
but then i realized this doesnt make sense . I thought about a sub query but it wont work either because we are selecting from two tables.
So i searched and could not find anything.
So the question is how do we "Find name of employees hired on different joining date?"
JOIN the Employees table with a subquery that counts the joining dates.
where j.num = 1
returns employees hired on different dates
where j.num > 1
returns employees hired on same date
select e.id, e.name, e.joining
from [SportsStore].[dbo].[Employees] e
inner join (select joining, count(*) num
from [SportsStore].[dbo].[Employees]
group by joining) j
on j.joining = e.joining
where j.num = 1;
+----+------+---------------------+
| id | name | joining |
+----+------+---------------------+
| 1 | abc | 01.01.2017 00:00:00 |
+----+------+---------------------+
| 2 | def | 01.01.2017 00:00:00 |
+----+------+---------------------+
| 5 | mno | 01.01.2017 00:00:00 |
+----+------+---------------------+
+----+------+---------------------+
| id | name | joining |
+----+------+---------------------+
| 3 | ghi | 02.01.2017 00:00:00 |
+----+------+---------------------+
| 4 | jkl | 03.01.2017 00:00:00 |
+----+------+---------------------+
Can check it here: http://rextester.com/OOO96554
If you just need the names (and not the list of different hiring dates), the following rather simple query should do the job:
select id, name
from employee
group by id, name
having count(distinct joining) > 1
after getting the answer , I have another way to get the same result . Here it is. I Hope its helpful to others and someone might explain which approach is better and in what scenario .
select name,joining from [SportsStore].[dbo].[Employees] where joining not in
(
select joining
from [SportsStore].[dbo].[Employees]
group by joining
having count(*)=1
)

Selecting Active roles from denormalized table with duplicates

I have a bit of a garbage table that I need to extract data from.
Name | Person# | Assignment_Status | Group
--------------------------------------------------
Smith, John | 1234567 | NLE | G1
Smith, John | 1234567 | Active | G2
Jones, Jane | 7654321 | Active | G1
James, Jack | 9876541 | LOA | G3
Peep, Laura | 6549871 | ServiceLOA | G1
Some, One | 3219875 | NLE | G2
Every time a person moves groups their current assignment_status gets set to NLE and a new record gets create to set the assignment_status to Active for the new group. When a person leaves the company they also set the assignment_status to NLE. This table does not have a Unique row ID nor does it have a date stamp.
I need a query that reduces the table to 1 record per employee and if the employee has multiple records I need the Assignment_Status that is not NLE. For example, John Smith should show as active for G2.
My first attempt was:
SELECT *
INTO #TempAssignments
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY aID) AS ID,
Name,
Person#,
(CASE WHEN Assignment_Status='NLE' THEN 1 ELSE 0 END) AS aID,
Group
FROM
tblAssignments)
With the data in a temp table then I created a second query to select the MIN of ID, the MIN of aID and GROUP BY Name and Person# then joined that back to the temp table to get the Group for the given ID.
This seems to work however this is a solution that needs to be deployed in multiple reports so I was wondering if there isn't a more compact way of doing this.
The following query:
SELECT Name, Person#, [Group]
FROM (
SELECT Name, Person#, [Group],
ROW_NUMBER() OVER (PARTITION BY Person#, Name
ORDER BY CASE
WHEN Assignment_Status <> 'NLE' THEN 0
ELSE 1
END) AS rn
FROM tblAssignments ) t
WHERE t.rn = 1
will select one record for each employee, as identified by a Person#, Name value pair. If the employee has multiple records, then a record with Assignment_Status that is not NLE will be selected.

SQL: Select distinct sum of column with max(column)

I have a salary table like this:
id | person_id | start_date | pay
1 | 1234 | 2012-01-01 | 3000
2 | 1234 | 2012-05-01 | 3500
3 | 5678 | 2012-01-01 | 5000
4 | 5678 | 2013-01-01 | 6000
5 | 9101 | 2012-09-01 | 2000
6 | 9101 | 2014-04-01 | 3000
7 | 9101 | 2011-01-01 | 1500
and so on...
Now I want to query the sum of the salaries of a specific month for all persons of a company.
I already have the ids of the persons who worked in the specific month in the specific company, so I can do something like WHERE person_id IN (...)
I have some problems with the salaries query though. The result for e.g. the month 2012-08 should be:
10000
which is 3500+5000+1500.
So I need to find the summed up pay value (for all persons in the IN clause) for the maximum start_date <= the specific month.
I tried various INNER JOINS but it's been a long day and I can't think straight at the moment.
Any hint is highly appreciated.
You need to get the active record. This following does this by calculating the max start date before the month in question:
select sum(s.pay)
from (select person_id, max(start_date) as maxstartdate
from salary
where person_id in ( . . . ) and
start_date < <first day of month of interest>
group by person_id
) p join
salary s
on s.person_id = p.person_id and
s.maxstartdate = p.start_date
You need to fill in the month and list of ids.
You can also do this with ranking functions, but you don't specify which SQL engine you are using.
You have to use group by for these things....
select person_id,sum(pay) from salary where person_id in(...) group by person_id
may it will helps you.....