Fetch DISTINCT Record with JOIN - sql

I want to take a distinct patient records and show it in a grid
Patient Table
PatientId, FirstName, LastName, City
Booking Table
BookingId, PatientId, CategoryId, BookingDate
Running below query gives duplicate patient records. This is happening because I have 3 bookings for same patient for different date. This query makes join and brings 3 patient records instead of 1 patient record.
SELECT DISTINCT PAT.PatientId
,PAT.FirstName
,PAT.LastName
,ROW_NUMBER() OVER (ORDER BY PAT.PatientId DESC) AS RowNumber
INTO #itemSearch
FROM dbo.Patient AS PAT
INNER JOIN dbo.Booking AS B
ON PAT.PatientId = B.PatientId WHERE B.CategoryId = 1
If I remove this line I get only 1. But I need this temp table for other paging process.
ROW_NUMBER() OVER (ORDER BY PAT.PatientId DESC) AS RowNumber INTO #itemSearch
How can I get only 1 patient even though they have 3 bookings?

Use GROUP BY clause
SELECT PAT.PatientId
,PAT.FirstName
,PAT.LastName
,ROW_NUMBER() OVER (ORDER BY PAT.PatientId DESC) AS RowNumber
INTO #itemSearch
FROM dbo.Patient AS PAT
INNER JOIN dbo.Booking AS B
ON PAT.PatientId = B.PatientId
WHERE B.CategoryId = 1
GROUP BY PAT.PatientId, PAT.FirstName, PAT.LastName
OR use DISTINCT with DENSE_RANK() ranking function
SELECT DISTINCT PAT.PatientId
,PAT.FirstName
,PAT.LastName
,DENSE_RANK() OVER (ORDER BY PAT.PatientId DESC) AS RowNumber
INTO #itemSearch
FROM dbo.Patient AS PAT
INNER JOIN dbo.Booking AS B
ON PAT.PatientId = B.PatientId
WHERE B.CategoryId = 1

Related

Get top one record of difference date

I try to get just one record of last Order of everysingle customer. ATM I have this idea but it giving me all of them Date of orders.
I work on dataBase AdventurerWorks2012
SELECT
H.CustomerID AS Customer,
H.SalesOrderID AS OrderNumber,
MAX(CAST(H.OrderDate AS DATE)) AS DateOrder
FROM
Sales.SalesOrderHeader H
JOIN
Sales.SalesOrderDetail D
ON H.SalesOrderID = D.SalesOrderID
GROUP BY
H.CustomerID,
H.SalesOrderID
ORDER BY
CustomerID;
You can use TOP 1 WITH TIES
select top 1 with ties
*
From
Sales.SalesOrderHeader H
INNER JOIN Sales.SalesOrderDetail D
ON H.SalesOrderID = D.SalesOrderID
ORDER BY row_number() over (partition by H.CustomerID order by H.OrderDate desc)
This applies and ordering with row_number() giving each customer's order an ID starting at 1 and going to N based off the OrderDate. The WITH TIES allows us to return the TOP 1 for each customer.
Another way, is using a CTE
;with cte as(
select top 1 with ties
*, RN = row_number() over (partition by H.CustomerID order by H.OrderDate desc)
From
Sales.SalesOrderHeader H
INNER JOIN Sales.SalesOrderDetail D
ON H.SalesOrderID = D.SalesOrderID)
select *
from cte
where RN = 1
I think you don't need to do JOIN:
SELECT h.*
FROM Sales.SalesOrderHeader h
WHERE OrderDate = (SELECT MAX(h1.OrderDate)
FROM Sales.SalesOrderHeader h1
WHERE h.CustomerID = h1.CustomerID
);
By this you will get customers with most recent order information.
select top 1 with ties
H.CustomerID AS Customer,
H.SalesOrderID as OrderNumber,
H.OrderDate As DataLast
From
Sales.SalesOrderHeader H
INNER JOIN Sales.SalesOrderDetail D
ON H.SalesOrderID = D.SalesOrderID
ORDER BY row_number() over (partition by H.CustomerID order by H.OrderDate desc)
This work like i need. Thanks!

Using SQL how can I write a query to find the top 5 per category per month?

I am trying to get the Top 5 rows with the highest number for each category for a specific time interval such as a month. What I currently have returns 5 of the exact same descriptions for a category. I am trying to get the top five. This only happens when I try to sort it based on a time period.
WITH CustomerRank
AS
(SELECT
Count(*) AS "Count",
d.Item,
d.Description,
Name,
i.Type,
d.CreatedOn
FROM [dbo].i,
d,
dbo.b,
as,
a,
c
WHERE d.Inspection_Id = i.Id AND d.Inspection_Id = i.Id AND
b.Id = i.BuildingPart_Id AND b.as= Assessments.Id
AND as.Application_Id = a.Id AND a.Customer_Id = Customers.Id
group by d.Item, d.Description, Name, i.Type, d.CreatedOn
)
select * from (
SELECT "Count",Item,Description,Type,ROW_NUMBER() Over (PARTITION BY Name order by "Count" desc) AS RowNum, Name, CreatedOn
FROM CustomerRank
where CreatedOn > '2017-1-1 00:00:00'
) s where RowNum <6
Cheers
Try something like this:
WITH CustomerRank
AS
(SELECT
Count(*) AS "Count",
d.Item, d.Description, Name, i.Type
FROM dbo.Inspection i
INNER JOIN dbo.Details d ON d.Inspection_Id = i.Id
INNER JOIN dbo.BuildingParts b ON b.Id = i.BuildingPart_Id
INNER JOIN dbo.Assessments a ON a.Id = b.AssessmentId
INNER JOIN dbo.Applications ap ON ap.Id = a.Application_Id
INNER JOIN dbo.Customers c ON c.Id = a.Customer_Id
where CreatedOn > '2017-1-1 00:00:00'
group by d.Item, d.Description, Name, i.Type
)
select * from (
SELECT "Count",Item,Description,Type,ROW_NUMBER() Over (PARTITION BY Name order by "Count" desc) AS RowNum, Name
FROM CustomerRank
) s where RowNum <6
The idea is that the CreatedOn column must be removed from the GROUP BY clause (because if you keep it there, we would get a different row for each value of the CreatedOn column).
Also, it's better to use JOIN-s and aliases for each table.

Last order item in Oracle SQL

I need to list columns from customer table, the date from first order and all data from last one, in a 1:N relationship between customer and order tables. I'm using Oracle 10g.
How the best way to do that?
TABLE CUSTOMER
---------------
id NUMBER
name VARCHAR2(200)
subscribe_date DATE
TABLE ORDER
---------------
id NUMBER
id_order NUMBER
purchase_date DATE
purchase_value NUMBER
Here is one way of doing it, using the row_number function, one join, and on aggregation:
select c.*,
min(o.purchase_date) as FirstPurchaseDate,
min(case when seqnum = 1 then o.id_order end) as Last_IdOrder,
min(case when seqnum = 1 then o.purchase_date end) as Last_PurchaseDate,
min(case when seqnum = 1 then o.purchase_value end) as Last_PurchaseValue
from Customer c join
(select o.*,
row_number() over (partition by o.id order by purchase_date desc) as seqnum
from orders o
) o
on c.customer_id = o.order_id
group by c.customer_id, c.name, c.subscribe_date
It's not obvious how to join the customer table to the orders table (order is a reserved word in Oracle so your table can't be named order). If we assume that the id_order in orders joins to the id in customer
SELECT c.id customer_id,
c.name name,
c.subscribe_date,
o.first_purchase_date,
o.id last_order_id,
o.purchase_date last_order_purchase_date,
o.purchase_value last_order_purchase_value
FROM customer c
JOIN (SELECT o.*,
min(o.purchase_date) over (partition by id_order) first_purchase_date,
rank() over (partition by id_order order by purchase_date desc) rnk
FROM orders o) o ON (c.id = o.id_order)
WHERE rnk = 1
I'm confused by your field names, but I'm going to assume that ORDER.id is the id in the CUSTOMER table.
The earliest order date is easy.
select CUSTOMER.*, min(ORDER.purchase_date)
from CUSTOMER
inner join ORDER on CUSTOMER.id = ORDER.id
group by CUSTOMER.*
To get the last order data, join this to the ORDER table again.
select CUSTOMER.*, min(ORD_FIRST.purchase_date), ORD_LAST.*
from CUSTOMER
inner join ORDER ORD_FIRST on CUSTOMER.id = ORD_FIRST.id
inner join ORDER ORD_LAST on CUSTOMER.id = ORD_LAST.id
group by CUSTOMER.*, ORD_LAST.*
having ORD_LAST.purchase_date = max(ORD_FIRST.purchase_date)
Maybe something like this assuming the ID field in the Order table is actually the Customer ID:
SELECT C.*, O1.*, O2.purchase_Date as FirstPurchaseDate
FROM Customer C
LEFT JOIN
(
SELECT Max(purchase_date) as pdate, id
FROM Orders
GROUP BY id
) MaxPurchaseOrder
ON C.Id = MaxPurchaseOrder.Id
LEFT JOIN Orders O1
ON MaxPurchaseOrder.pdate = O1.purchase_date
AND MaxPurchaseOrder.id = O1.id
LEFT JOIN
(
SELECT Min(purchase_date) as pdate, id
FROM Orders
GROUP BY id
) MinPurchaseOrder
ON C.Id = MinPurchaseOrder.Id
LEFT JOIN Orders O2
ON MinPurchaseOrder.pdate = O2.purchase_date
AND MinPurchaseOrder.id = O2.id
And the sql fiddle.

oracle - maximum per group

University Table - UniversityName, UniversityId
Lease Table - LeaseId, BookId, UniversityId, LeaseDate
Book Table - BookId, UniversityId, Category, PageCount.
For each university, I have to find category that had the most number of books leased.
So, something like
UniversityName Category #OfTimesLeased
I have been playing around with it with some success using Dense_Rank etc - but if there is a tie, only one of them shows up, while I want both of them to show up.
Current Query:
select b.UniversityId, MAX(tempTable.type) KEEP (DENSE_RANK FIRST ORDER BY tempTable.counter DESC)
from book b
join
(select count(l.leaseid) AS counter, b.category, b.universityid
from lease l
join book b
on b.bookid =l.bookid AND b.universityid=r.universityid
group by b.category, b.universityid) tempTable
on counterTable.universityid= b.universityid
group by b.universityid
^Unable to solve the tie issue and get the number of leases for the most leased book type.
Try this
WITH CTE AS
(
SELECT UniversityName, Category, Count(*) NumOfTimesLeased
FROM University u
INNER JOIN Book b on u.UniversityId = b.UniversityId
INNER JOIN Lease l on b.bookid = l.bookid and b.UniversityId = l.UniversityId
GROUP BY UniversityName, Category
),
CTE2 AS (
SELECT UniversityName, Category, NumOfTimesLeased,
RANK() OVER (PARTITION BY UniversityName
ORDER BY NumOfTimesLeased DESC) Rnk
FROM CTE)
SELECT * FROM CTE2 WHERE Rnk = 1
You are on the right track with the analytic functions:
select Univerity, Category, NumLeased
from (select t.*,
row_number() over (partition by university order by Numleased desc) as seqnum
from (select l.university, b.category, count(*) as NumLeased
from lease l join
book b
on l.bookid = b.bookid
group by l.university, b.category
) t
) t
where seqnum = 1
I use the row_number() because you only want the one top value. Rank and dense_rank are more useful when you are looking for values other than "1".
If you want the top values to show up when there is a tie, then use dense_rank instead of row_number. The values will be on different rows.

SQL: improving join efficiency

If I turn this sub-query which selects sales persons and their highest price paid for any item they sell:
select *,
(select top 1 highestProductPrice
from orders o
where o.salespersonid = s.id
order by highestProductPrice desc ) as highestProductPrice
from salespersons s
in to this join in order to improve efficiency:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders ) o on s.id = o.salespersonid
It still touches every order record (it enumerates the entire table before filtering by salespersonid it seems.) However you cannot do this:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders
where orders.salepersonid = s.id) o on s.id = o.salespersonid
The where clause in the join causes a `multi-part identifier "s.id" could not be bound.
Is there any way to join the top 1 out of each order group with a join but without touching each record in orders?
Try
SELECT
S.*,
T.HighestProductPrice
FROM
SalesPersons S
CROSS APPLY
(
SELECT TOP 1 O.HighestProductPrice
FROM Orders O
WHERE O.SalesPersonid = S.Id
ORDER BY O.SalesPersonid, O.HighestProductPrice DESC
) T
would
select s.*, max(highestProductPrice)
from salespersons s
join orders o on o.salespersonid = s.id
group by s.*
or
select s.*, highestProductPrice
from salespersons s join (select salepersonid,
max(highestProductPrice) as highestProductPrice
from orders o) as o on o.salespersonid = s.id
work?