Create a subquery in a view SQL- ORACLE - sql

I am setting up a database view.
I have a table PERSON and a RELATION_COMPANY related by the person_id. I amb trying to obtain the min date from the relation_company for each person_id.
I need to use a subquery?
This is what I have tried so far.
CREATE OR REPLACE VIEW PEOPLE_ADDRESS
select
p.id as ADDRESS_CODE,
case p.type_
when 1 then 'JURIDICAL'
when 0 then 'NATURAL'
end PERSON_TYPE,
(select min(start_date) from relation_company where relation_company.person_id = p.id) as INITIAL_dATE
P.ID as PERSON_CODE,
p.ADDRESS_NAME as ADDRESS,
p.POSTAL_CODE as POSTAL_CODE,
case P.COUNTRY_ID
when 68 then 'NO'
else 'YES'
end FOREIGN_INDICATOR
from
PERSON p join relation_company r on relation_company = p.id ;
That's my person table:
ID TYPE_ ADDRESS_NAME NAME OTHER COLUMNS
1 0 AVENUE SMITH, 19 MICHAEL SCOTT ....
2 1 OXFORD STREET , 119 COMPANY A ....
3 1 CHESTHAM ROAD, 7 COMPANY B ....
That's the RELATION_COMPANY table:
ID PERSON_ID START_DATE
1 1 2016-12-12 00:00:00
2 1 1981-07-09 00:00:00
3 2 1991-10-10 00:00:00
4 2 1981-03-09 00:00:00
5 3 1984-07-05 00:00:00
6 3 1981-08-11 00:00:00
7 3 1987-07-09 00:00:00
8 3 2011-07-09 00:00:00
select min(START_DATE) from RELATION_COMPANY group by PERSON_ID;
The result should be:
ADDRESS_CODE PERSON_TYPE INITIAL_dATE other columns
1 NATURAL 1981-07-09 00:00:00 ....
2 JURIDICAL 1981-03-09 00:00:00 ....
3 JURIDICAL 1981-08-11 00:00:00
I want to get the min(start_date) for each person_id and print it to the view to the column INITIAL_dATE.
I am having trouble to create that query.

You Already have the solution, this query are missing the "person_id" column in your SELECT statement.
Change this query
select min(START_DATE) from RELATION_COMPANY group by PERSON_ID;
To this.
select min(START_DATE),PERSON_ID from RELATION_COMPANY group by PERSON_ID;
Hope this helps.

You don't need a subquery for your solution.
Only add a group-clause to your query.
like this one:
select
p.id as PERSON_ID,
case p.type_
when '1' then 'JURIDICAL'
when '0' then 'NATURAL'
end PERSON_TYPE,
min(start_date) as INITIAL_dATE,
p.ADDRESS_NAME as ADDRESS,
p.POSTAL_CODE as POSTAL_CODE,
case P.COUNTRY_ID
when 68 then 'NO'
else 'YES'
end FOREIGN_INDICATOR
from PERSON p join relation_company r on r.person_id = p.id
group by p.id, type_, ADDRESS_NAME, POSTAL_CODE, P.COUNTRY_ID;

Related

Sub query for two tables base on status using SQL

I have two tables Patient and PatientStatusLog using SQL tables
1.Patient
sno patientid status createdby Reegion
1 481910 D 1222 India
2 476795 D 1222 India
2.PatientStatusLog
sno patientid status comments createdby CreatedDate(dd/mm/yyyy)
1 481910 A mycommnet 1222 01/01/2000
2 481910 A mycommnet 1222 02/01/2000
3 481910 B mycommnet 1222 01/01/2000
4 481910 C mycommnet 1222 01/01/2000
I need output like below who have status A and createddate should recent pass date from PatientStatusLog table using patientid of both tables
Region status CreatedDate
India A 02/01/2000
You can use window functions:
select . . . -- whatever columns you want
from Patient p join
(select psl.*,
rank() over (partition by patientid order by createddate desc) as seqnum
from PatientStatusLog psl
) psl
on p.patientid = psl.patientid
where psl.seqnum = 1 and psl.status = 'A';
Note that this uses rank() because the creation date appears to have duplicates.
Try this:
SELECT TOP 1
p.Region,
psl.Status,
psl.CreatedDate
FROM
Patient [p]
JOIN PatientStatusLog [psl] ON psl.patientid = p.patientid
AND psl.status = 'A'
ORDER BY
psl.CreatedDate DESC
For the subquery request:
SELECT
p.Region,
x.status,
x.CreatedDate
FROM
(
SELECT TOP 1
PatientId,
Status,
CreatedDate
FROM
PatientStatusLog
WHERE
status = 'A'
ORDER BY
CreatedDate DESC
) AS x
JOIN Patient [p] ON p.PatientId = x.PatientId
Note that this is definitely not the best way to handle this, both from a code readability and optimization perspective.

How to write a sql script for a range of Oracle assignment date records by different employee's job titles

I am trying to write an ad-hoc query for a range of assignment date records by employee's job title. These examples are used for the Oracle application assignment table.
First sample:
AsgId Start_Date End_Date Job_ID
1 1/1/14 6/30/14 10
1 7/1/14 11/15/14 10
1 11/16/14 1/10/15 20
1 1/11/15 3/10/15 10
1 3/11/15 3/31/15 10
1 4/1/15 12/31/18 20
I have tried analytical functions, in-line views, and other code without success.
Expected report results of 3 date-range records by job title:
asgid start_date end_date job_title
1 1/1/14 11/15/14 10
1 11/16/14 1/10/15 20
1 1/11/15 3/31/15 10
1 4/1/15 12/31/18 20
Second sample:
EMP_ID START_DATE END_DATE JOB_TITLE
1 1/1/14 11/15/14 10
1 11/16/14 11/10/15 10
1 11/11/15 12/31/15 20
1 1/1/16 1/31/16 10
1 2/1/16 12/31/16 10
Expected report results of 3 date-range records by job title
EMP_ID START_DATE END_DATE JOB_TITLE
1 1/1/14 11/10/15 10
1 11/11/15 12/31/15 20
1 1/1/16 12/31/16 10
This is a type of gaps-and-islands problem. Assuming that there are no gaps or overlaps, you can use left join and a cumulative sum to determine the islands. The rest is aggregation:
select asgid, job_id, min(start_date) as start_date,
max(end_date) as end_date
from (select a.*,
sum(case when aprev.asgid is null then 1 else 0 end) over (partition by a.asgid, a.job_id order by a.start_date) as grp
from assignment a left join
assignment aprev
on aprev.asgid = a.asgid and
aprev.job_id = a.job_id and
aprev.end_date = a.start_date - 1
) a
group by asgid, job_id, grp
order by asgid, min(a.start_date);
Here is a db<>fiddle.

SQL Server - select min date and id from foreign key

These are my tables:
USER:
id_user name email last_access id_company
1 jhonatan abc#abc.com 2014-12-15 1
2 cesar cef#cef.com 2014-12-31 1
3 john 123#123.com 2015-01-09 2
4 steven 897#asdd.cpom 2015-01-02 2
5 greg sd#touch.com 2014-12-07 1
6 kyle fb#fb.com 2014-11-20 1
COMPANY:
id_company company
1 Facebook
2 Appslovers
I need to know, what are the users which has the MIN last_access per company (just one). It could be like this:
id_user name last_access company
6 kyle 2014-11-20 Facebook
4 steven 2015-01-02 Appslovers
Is it possible ?
Use window function
SELECT id_user,
NAME,
last_access,
company
FROM (SELECT id_user,
NAME,
last_access,
company,
Row_number()OVER(partition BY company ORDER BY last_access) rn
FROM users u
JOIN company c
ON u.id_company = c.id_company) a
WHERE rn = 1
or join both the tables find the min last_access date per company then join the result back to the users table to get the result
SELECT id_user,
NAME,
a.last_access,
a.company
FROM users u
JOIN(SELECT u.id_company,
Min(last_access) last_access,
company
FROM users u
JOIN company c
ON u.id_company = c.id_company
GROUP BY u.id_company,
company) a
ON a.id_company = u.id_company
AND u.last_access = a.last_access
This can be done in many ways, for example by using a window function like row_number to partition the data and then selecting the top rows from each group like this:
;with cte (id_user, name, last_access, company, seq) as (
select
id_user,
name,
last_access,
company,
seq = row_number() over (partition by u.id_company order by last_access)
from [user] u
inner join [company] c on u.id_company = c.id_company
)
select id_user, name, last_access, company
from cte where seq = 1

INNER JOIN on CTE (Common Table Expression) Without PK

I have a CTE in which I am finding duplicate records matching on 5 columns:
;WITH DuplicateCount AS
(
SELECT
FirstName,
LastName,
DateofBirth,
Email,
c1.Status,
Count(*) AS TotalCount
FROM Customer c
INNER JOIN Customer_1 c1 ON c1.customerID = c.customerID
GROUP BY FirstName, LastName, DateofBirth, Email, c1.Status
HAVING COUNT(*) > 1
)
I am then selecting Status and TotalCount from that CTE and joining an Enum table to produce readable data
;WITH DuplicateCount AS
(
SELECT
FirstName,
LastName,
DateofBirth,
Email,
c1.Status,
Count(*) AS TotalCount
FROM Customer c
INNER JOIN Customer_1 c1 ON c1.customerID = c.customerID
GROUP BY FirstName, LastName, DateofBirth, Email, c1.Status
HAVING COUNT(*) > 1
)
SELECT e.Display, dc.TotalCount
FROM DuplicateCount dc
INNER JOIN Enum e ON dc.Status = e.Index
In this scenario, I am able to pull back readable data and use Excel to spit out a graph report of duplicates by Status.
Problem
I need to join the Customer_1 table once again to gather one more column: Stage. Here is how I tried to do it:
;WITH DuplicateCount AS
(
SELECT customerID,
FirstName,
LastName,
DateofBirth,
Email,
c1.Status,
Count(*) AS TotalCount
FROM Customer c
INNER JOIN Customer_1 c1 ON c1.customerID = c.customerID
GROUP BY customerID, FirstName, LastName, DateofBirth, Email, c1.Status
HAVING COUNT(*) > 1
)
SELECT e.Display,
CASE
WHEN c1.Stage = 6 THEN 'First'
WHEN c1.Stage = 7 THEN 'Second'
WHEN c1.Stage = 8 THEN 'Third'
WHEN c1.Stage = 11 THEN 'Fourth'
WHEN c1.Stage = 9 THEN 'Fifth'
WHEN c1.Stage = 10 THEN 'Sixth'
WHEN c1.Stage = 12 THEN 'Unknown'
ELSE ''
END AS Stage,
dc.TotalCount
FROM DuplicateCount dc
INNER JOIN Enum e ON dc.Status = e.Index
INNER JOIN Customer_1 c1 ON c1.customerID = dc.customerID
Obviously, that didn't work because none of my records will have duplicate PKs.
Is there a way to join a table to my CTE without a PK? Or somehow add a PK to my CTE without grouping by it?
Edit: This is what I am trying to achieve
|FirstName | LastName | Stage | Total Count
| John | Smith | First | 2
| John | Smith | Third | 2
| Alex | Smith | First | 2
| Jane | Smith | Third | 2
| Jane | Smith | First | 2
| Jack | Smith | Second | 2
Then, when reporting on this data:
John Smith has 4 total records. Two in First, two in Third
Alex Smith has 2 total records. Two in First
Jane Smith has 4 total records. Two in First and two in Third
Jack Smith has 2 total records. Two in Second.
When graphing this data, I should be able to see:
First: 6 total.
Second: 2 total.
Third: 4 total.
Ideally, I could then also bring in CreatedDate and begin to gather data-over-time reports for:
How many duplicates per Stage.
How many duplicates per Person.
How many duplicates for specific date ranges, events, etc.
The cardinality of the two sets of data don't match. By that I mean the first set of data with the identified duplicates in is aggregated data across a number of customers (without identifying any customers). You can't then take the multiple separate Customer IDs and attribute them back to the aggregated rows.
I think what you need to do is re-frame what you are trying to get out of your data and work backwards. Post an example set of results that you are trying to achieve.
UPDATE:
It seems you want a list of customer\stage groups with counts?:
SELECT customerID,
FirstName,
LastName,
DateofBirth,
Email,
c1.Status,
CASE
WHEN c1.Stage = 6 THEN 'First'
WHEN c1.Stage = 7 THEN 'Second'
WHEN c1.Stage = 8 THEN 'Third'
WHEN c1.Stage = 11 THEN 'Fourth'
WHEN c1.Stage = 9 THEN 'Fifth'
WHEN c1.Stage = 10 THEN 'Sixth'
WHEN c1.Stage = 12 THEN 'Unknown'
ELSE ''
END AS Stage,
Count(*) AS TotalCount
FROM Customer c
INNER JOIN Customer_1 c1 ON c1.customerID = c.customerID
GROUP BY customerID, FirstName, LastName, DateofBirth, Email, c1.Status, c1.Stage
HAVING COUNT(*) > 1

SQL Group By Help Required

I have a table named People in the following format:
Date | Name.
When I count the people by Grouping By Name with
Select Date, Name, count(*)
From People
Group By Date, Name;
Will give the following
Date Name count(*)
10 Peter 25
10 John 30
10 Mark 25
11 Peter 15
11 John 10
11 Mark 5
But I would like the following result:
Date Peter John Mark
10 25 30 25
11 15 10 5
Is this possible? This is a simple example of a more complicated database. If someone helps me in solving this problem I will use the concept to implement it in my table
Thanks!
Select Date
, count(case when Name = 'Peter' then 1 else null end)
, count(case when Name = 'John' then 1 else null end)
, count(case when Name = 'Mark' then 1 else null end)
From People
Group By Date;
another option different from turbanoff's if, for some reason, you find yourself in a situation that you cant apply a group by:
Select distinct(P.Date),
(select count(*) from People where date=p.date and name='Peter') as Peter,
(select count(*) from People where date=p.date and name='John') as John,
(select count(*) from People where date=p.date and name='Mark') as Mark
From People P