Group by with left outer join, exclude nulls - sql

I have an order table and a trip table that includes payment information. It is a many to many relationship - order can split across many trips and one trip could have payment info for a couple of orders and sometimes an order. There is no "Zero" record in the Trips table - so a left join with that value as the key will return a NULL record. I am using SQL 2012
Order table
+----+----------+--------------+
| order_id | trip_nbr | veh_id |
+----+----------+--------------+
| 1 | 12 | 3 |
| 2 | 22 | 6 |
| 2 | 0 | 8 |
| 4 | 25 | 7 |
| 7 | 0 | 11 |
+----+----------+--------------+
Trips table
+----+------------+--------------+
| trip_nbr | payment | veh_id |
+----+------------+--------------+
| 12 | 20.00 | 3 |
| 22 | 123.00 | 6 |
| 22 | 12.50 | 6 |
| 25 | 133.33 | 7 |
+----+------------+--------------+
Here is my query:
select o.order_id,
t.trip_nbr,
sum(t.payment_amt)
from orders o
left outer join trips t
on o.trip_nbr = t.trip_nbr
group by o.order_id,
t.trip_nbr
Results:
+----+----------+--------------+
| order_id | trip_nbr | sum |
+----+----------+--------------+
| 1 | 12 | 20.00 |
| 2 | 22 | 135.50 |
| 2 | NULL | NULL |
| 4 | 25 | 133.33 |
| 7 | NULL | NULL |
+----+----------+--------------+
The problem is, I get lots of information from the orders table and just the payment info from the Trips table. So I don't want to exclude any order records (which would happen if I add the clause "WHERE t.trip_nbr is NOT NULL") - but I don't want to get 2 records in my grouping - one for t.trip_nbr is NULL and one where it finds a match.
Desired results:
+----+----------+--------------+
| order_id | trip_nbr | sum |
+----+----------+--------------+
| 1 | 12 | 20.00 |
| 2 | 22 | 135.50 |
| 4 | 25 | 133.33 |
| 7 | NULL | NULL |
+----+----------+--------------+
I want the unmatched record order_id = 2 to be "summarized away" - but keep the lone record for order_id = 7. The reason is that this table is later join with another table and the extra NULL records are creating duplicates.

This should work:
WITH orders2 AS
(
SELECT *,
N = SUM(CASE WHEN trip_nbr <> 0 THEN 1 ELSE 0 END) OVER(PARTITION BY order_id)
FROM orders
)
SELECT o.order_id,
t.trip_nbr,
SUM(t.payment_amt)
FROM orders2 o
LEFT OUTER JOIN trips t
ON o.trip_nbr = t.trip_nbr
WHERE N = 0 OR (N > 1 AND o.trp_nbr <> 0)
GROUP BY o.order_id,
t.trip_nbr;

You can use a window function like RANK to identify superfluous NULL- valued records and filter them out in an outer query:
select order_id,
trip_nbr,
total_payment
from (
select o.order_id,
t.trip_nbr,
sum(t.payment) as total_payment,
rank() over (partition by order_id
order by case
when t.trip_nbr IS NULL then 2
else 1
end) as rnk
from orders o
left outer join trips t
on o.trip_nbr = t.trip_nbr
group by o.order_id, t.trip_nbr) as t
where t.rnk = 1

If you convert your nulls to zero, then sum "trip_nbr" and "sum" for a given order_id. Would this not solve your challenge?
create table #Order (Order_Id int , Trip_nbr int , Veh_id int )
Create Table #Trips (trip_nbr int , Payment Numeric(13,2), Veh_id int )
insert into #Order (Order_id, Trip_nbr, Veh_id) values (1,12,3)
insert into #Order (Order_id, Trip_nbr, Veh_id) values (2,22,6)
insert into #Order (Order_id, Trip_nbr, Veh_id) values (2,0 ,8)
insert into #Order (Order_id, Trip_nbr, Veh_id) values (4,25,7)
insert into #Order (Order_id, Trip_nbr, Veh_id) values (7,0,11)
insert into #Trips (trip_nbr, Payment, Veh_id) values (12, 20.00 , 3 )
insert into #Trips (trip_nbr, Payment, Veh_id) values (22, 123.00,6 )
insert into #Trips (trip_nbr, Payment, Veh_id) values (22, 12.50 , 6 )
insert into #Trips (trip_nbr, Payment, Veh_id) values (25, 133.33 , 7 )
select Order_id, trip_nbr = sum(trip_nbr), Payment = sum(payment)
from
(
select o.order_id,
t.trip_nbr,
Payment = sum(t.Payment)
from #order o
left outer join #trips t on t.trip_nbr = o.trip_nbr
-- left outer join #order o on t.trip_nbr = o.trip_nbr
group by o.order_id, t.trip_nbr
) x
group by Order_id
order by Order_id

Related

SQL left join with latest record

I want to left join a table with the latest record only.
I have Customer1 table:
+--------+----------+
| CustID | CustName |
+--------+----------+
| 1 | ABC123 |
| 2 | 456XYZ |
| 3 | 5PQR3 |
| 4 | 789XYZ |
| 5 | 789A |
+--------+----------+
SalesInvoice table:
+------------+--------+-----------+
| InvDate | CustID | InvNumber |
+------------+--------+-----------+
| 2020-03-01 | 1 | IV236 |
| 2020-04-07 | 1 | IV644 |
| 2020-06-13 | 2 | IV869 |
| 2020-03-29 | 3 | IV436 |
| 2020-02-06 | 3 | IV126 |
+------------+--------+-----------+
And I want this required output:
+--------+------------+-----------+
| CustID | InvDate | InvNumber |
+--------+------------+-----------+
| 1 | 2020-04-07 | IV644 |
| 2 | 2020-06-13 | IV869 |
| 3 | 2020-03-29 | IV436 |
| 4 | | |
| 5 | | |
+--------+------------+-----------+
For quick and easy, below is the sample code.
drop table if exists #Customer1
create table #Customer1(CustID int, CustName varchar (100))
insert into #Customer1 values
(1,'ABC123'),
(2,'456XYZ'),
(3,'5PQR3'),
(4,'789XYZ'),
(5,'789A')
drop table if exists #SalesInvoice
create table #SalesInvoice(InvDate DATE, CustID INT, InvNumber varchar (100))
insert into #SalesInvoice values
('2020-03-01',1,'IV236'),
('2020-04-07',1,'IV644'),
('2020-06-13',2,'IV869'),
('2020-03-29',3,'IV436'),
('2020-02-06',3,'IV126')
I like using TOP 1 WITH TIES in this case:
SELECT TOP 1 WITH TIES c.CustID, i.InvDate, i.InvNumber
FROM #Customer1 c
LEFT JOIN #Invoices i ON c.CustID = i.CustID
ORDER BY ROW_NUMBER() OVER (PARTITION BY c.CustID ORDER BY i.InvDate DESC);
Demo
The top 1 trick here is to order by row number, assigning a sequence to each customer, with the sequence descending by invoice date. Then, this approach retains just the most recent invoice record for each customer.
I recommend outer apply:
select c.*, i.*
from #c c outer apply
(select top (1) i.*
from #invoices i
where i.custId = c.custId
order by i.invDate desc
) i;
outer apply implements a special type of join called a "lateral join". This is a very powerful construct. But when learning about them, you can think of a lateral join as a correlated subquery that can return more than one column and more than one row.
You can try ROW_NUMBER window function instead of lateral joins with this simple self-explaining T-SQL
SELECT c.CustID
, d.InvDate
, d.InvNumber
FROM #C c
LEFT JOIN (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY CustID ORDER BY InvDate DESC) AS RowNo
FROM #D
) d
ON c.CustID = d.CustID
AND d.RowNo = 1
Basically ROW_NUMBER is used to filter the "last" invoice in one table scan, instead of performing SELECT TOP 1 ... ORDER BY in the correlated query which has to be executed multiple times -- as much as the number of customers.

SQL Server Display total value in separate column where group by already breaks down the total

Sorry for the convoluted title!
I have a table called bookings as follows:
|Booking_id|Client_Id|Supplier_id|
----------------------------------
| 1 | 8 | 123 |
| 2 | 54 | 354 |
| 3 | 54 | 100 |
| 4 | 8 | 123 |
| 5 | 79 | 64 |
| 6 | 123 | 354 |
| 7 | 8 | 354 |
| 8 | 54 | 100 |
| 9 | 123 | 354 |
| 10 | 22 | 123 |
I have to get the % of total bookings per supplier per client
So output should be:
% of total bookings per supplier per client
|Client_id|Supplier_id|NumOfBookings|%_ofClientBookings|
--------------------------------------------------------
| 8 | 123 | 2 | 66 |
| 8 | 354 | 1 | 33 |
| 22 | 123 | 1 | 100 |
| 54 | 354 | 1 | 50 |
| 54 | 100 | 1 | 50 |
| 79 | 64 | 1 | 100 |
| 123 | 354 | 2 | 100 |
The query I have is as follows:
SELECT Client_id, COUNT(Booking_id) As NumOfBookings, Supplier_id,
FROM Booking
GROUP BY Client_id, Supplier_id
ORDER BY Client_id asc, NumOfBookings desc
I need to GROUP_BY Supplier_id and Client_id to see the number of bookings per supplier per client, but then I can't display the % of total bookings per client in the same table because the total booking figure isn't available to me. I can't seem to solve this, any suggestions?
I was able to get the desired result using derived tables, one for the number of bookings by supplier and client, and one for the total bookings by client.
Below is the code I used, you would need to replace #table with your table and ignore the inserts/create table.
IF OBJECT_ID('tempdb..#table') IS NOT NULL
BEGIN
DROP TABLE #table
END
CREATE TABLE #table(Booking_id int, Client_id int, supplier_id int)
insert into #table values(1,8,123)
insert into #table values(2,54,354)
insert into #table values(3,54,100)
insert into #table values(4,8,123)
insert into #table values(5,79,64)
insert into #table values(6,134,354)
insert into #table values(7,8,354)
insert into #table values(8,54,100)
insert into #table values(9,123,354)
insert into #table values(10,22,123)
SELECT NoB.Client_id,NoB.supplier_id,NoB.NumOfBookings, (NoB.NumOfBookings/(NULLIF(Tot.NumOfBookings,0) *1.0)) *100 FROM
(
SELECT Client_id, COUNT(Booking_id) As NumOfBookings, Supplier_id
FROM #table
GROUP BY Client_id, Supplier_id
) NoB JOIN
(
SELECT Client_id, COUNT(Booking_id) As NumOfBookings
FROM #table
GROUP BY Client_id
) Tot on Tot.Client_id=NoB.Client_id
Use "with" or multiple inline views, i.e. queries in from clause. With is simpler.
With bookbyclient as (select that has total for client. Key for result is client)
Now select like you have but join to bookbyclient. In select list divide booking value here by the total from above to get percent
This script will help-
SELECT A.Client_Id,A.Supplier_id,A.C NumOfBookings,
CAST((A.C*1.0)/B.C*100.00 AS INT) AS Percentage
FROM
(
SELECT Client_Id,Supplier_id ,COUNT(Booking_id) C
FROM your_table
GROUP BY Client_Id,Supplier_id
)A
INNER JOIN
(
SELECT Client_Id,COUNT(Booking_id) C
FROM your_table
GROUP BY Client_Id
)B
ON A.Client_Id = B.Client_Id
Try something like this:
SELECT Client_id,
COUNT(Booking_id) As NumOfBookings,
FLOOR(100*(CAST(COUNT(Booking_id) AS FLOAT)/(SELECT COUNT(Booking_id) FROM Booking bb
WHERE bb.Client_id = b.Client_id))),
Supplier_id
FROM Booking b
GROUP BY Client_id, Supplier_id
ORDER BY Client_id asc, NumOfBookings desc
Live Demo.
Try this
SELECT Client_id, NumOfBookings, Supplier_id
, (CAST(NumOfBookings AS DECIMAL(5, 2)) / SUM(NumOfBookings) OVER(PARTITION BY Client_id)) * 100 AS [%_ofClientBookings]
FROM (
SELECT Client_id, COUNT(Booking_id) As NumOfBookings, Supplier_id
FROM Booking
GROUP BY Client_id, Supplier_id
) a
ORDER BY Client_id asc, NumOfBookings desc

Joining two tables + third table as a conditional column

If i have three tables called warehouse , warehouse_order and table called warehouse_fulfillment. A warehouse order is created by warehouse admin, initially has 0 fulfillment records, can have many warehouse_fulfillment records with failed/rejected statuses, and only one success state (it's done):
-- Warehouse
+---------------------------------------+-----------+----------+
| id | name | location |
+---------------------------------------+-----------+----------+
| 9bcae08e-ad36-4d97-b9ec-4857714e902a | "big" | "MLB" |
+---------------------------------------+-----------+----------+
| b442e783-4725-41e9-af83-f75004ee1b38 | "bigger" | "MLB" |
+---------------------------------------+-----------+----------+
| 986d5aa9-0523-42d8-b183-dfd546d3e682 | "biggest" | "MLB" |
+---------------------------------------+-----------+----------+
-- Warehouse_order Table
+---------------------------------------+--------------------------------------+--------+----------+
| id | warehouse_id | type | quantity |
+---------------------------------------+--------------------------------------+--------+----------+
| 9cb99fd9-9e5e-4240-8162-d28747be01cd | b442e783-4725-41e9-af83-f75004ee1b38 | BN_100 | 100 |
+---------------------------------------+-------------------------------------+--------+-----------+
| eceb0b5a-5afa-40e4-ac62-efb686e3bdae | 9bcae08e-ad36-4d97-b9ec-4857714e902a | BN_200 | 400 |
+---------------------------------------+--------------------------------------+--------+----------+
| 13370467-cf0c-47f2-8fea-a215500607e6 | 986d5aa9-0523-42d8-b183-dfd546d3e68 | BN_300 | 10 |
+---------------------------------------+--------------------------------------+--------+----------+
-- Warhouse_fulfillment Table
+---------------------------------------+---------------------------------------+------------+
| id | order_id | status |
+---------------------------------------+---------------------------------------+------------+
| 8a69edde-2346-48b8-96d0-6c4e25527f38 | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "FAILLED" |
+---------------------------------------+---------------------------------------+------------+
| a2006a64-9bdc-4bfa-ba14-a44769aeb4a2 | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "REJECTED" |
+---------------------------------------+---------------------------------------+------------+
| bf0aa1fc-6dfc-4fd0-ba20-be101b1985d1 | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "FAILED" |
+---------------------------------------+---------------------------------------+------------+
| 48c7d747-2f9b-4535-8f27-210a43cf5c30 | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "SUCCESS" |
+---------------------------------------+---------------------------------------+------------+
| 7f8e18c9-4322-428a-9370-9ecd1c5ef286 | 13370467-cf0c-47f2-8fea-a215500607e6 | "FAILED" |
+---------------------------------------+---------------------------------------+------------+
I want to query the above records in such a way that result looks like so:
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
| id | name | location | order_id | order_type | order_quantity | fulfillment_id |
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
| 9bcae08e-ad36-4d97-b9ec-4857714e902a | "big" | "MLB" | eceb0b5a-5afa-40e4-ac62-efb686e3bdae | "BN_100" | 100 | NULL |
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
| b442e783-4725-41e9-af83-f75004ee1b38 | "bigger" | "MLB" | 9cb99fd9-9e5e-4240-8162-d28747be01cd | "BN_200" | 400 | 48c7d747-2f9b-4535-8f27-210a43cf5c30 |
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
| 986d5aa9-0523-42d8-b183-dfd546d3e682 | "biggest" | "MLB" | 13370467-cf0c-47f2-8fea-a215500607e6 | "BN_300" | 10 | NULL |
+--------------------------------------+-----------+----------+---------------------------------------+------------+----------------+--------------------------------------+
I couldn't do this without the repeated rows in cases where an order has multiple failed statuses.
Did you try SELECT DISTINCT? Because you don't have the status column (which causes the duplicates) in the select list, this should work.
SELECT DISTINCT W.id, W.name, W.location, WO.id order_id, WO.type order_type, WO.quantity order_quantity, WF.id fulfillment_id
FROM warehouse W
LEFT JOIN warehouse_order WO ON W.id = WO.warehouse_id
LEFT JOIN warehouse_fulfillment WF on WF.order_id = WO.id
Otherwise, I would need to know what DBMS the SQL is for, but every flavor I've worked with has some way to rank/order results with a partition so that you can take just the first record based on some key, for example:
SELECT id, name, location, order_id, order_type, order_quantity
FROM (
SELECT W.id, W.name, W.location, WO.id order_id, WO.type order_type, WO.quantity order_quantity, WF.id fulfillment_id, ROW_NUMBER() OVER (PARTITION BY WO.id ORDER BY WF.ID) rNum
FROM warehouse W
LEFT JOIN warehouse_order WO ON W.id = WO.warehouse_id
LEFT JOIN warehouse_fulfillment WF on WF.order_id = WO.id
) A
WHERE rNum = 1
It would be better to order by date DESC or something like that to get the most recent record.
Without some reliable method within the fulfillment table to determine "latest status", such as a timestamp, you will need to choose some arbitrary method to arrive at a priority order amongst the possible status values. Below I have used a case expression within the over clause so that "success" will be get the row number 1 if that status exists for an order. Adjust the case expression as you see fit for the other possible values of that column.
When the subquery containing the row number is joined to the main query that join includes and rn=1 so only fulfillment row per order will be possible.
Please note that in the sample data there is a missing warehouse row so I had to use a left join, but I expect it would be an inner join in the real db.
SQL Fiddle Demo
CREATE TABLE Warehouse
(ID varchar(36), Name varchar(9), Location varchar(5))
;
INSERT INTO Warehouse
("id", "name", "location")
VALUES
('9bcae08e-ad36-4d97-b9ec-4857714e902a', 'big', 'MLB'),
('b442e783-4725-41e9-af83-f75004ee1b38', 'bigger', 'MLB'),
('986d5aa9-0523-42d8-b183-dfd546d3e682', 'biggest', 'MLB')
;
CREATE TABLE Warehouse_order
(ID varchar(36), Warehouse_id varchar(36), type varchar(6), quantity int)
;
INSERT INTO Warehouse_order
("id", "warehouse_id", "type", "quantity")
VALUES
('9cb99fd9-9e5e-4240-8162-d28747be01cd', 'b442e783-4725-41e9-af83-f75004ee1b38', 'BN_100', 100),
('eceb0b5a-5afa-40e4-ac62-efb686e3bdae', '9bcae08e-ad36-4d97-b9ec-4857714e902a', 'BN_200', 400),
('13370467-cf0c-47f2-8fea-a215500607e6', '986d5aa9-0523-42d8-b183-dfd546d3e68', 'BN_300', 10)
;
CREATE TABLE Warehouse_fulfillment
(ID varchar(36), Order_id varchar(36), Status varchar(10))
;
INSERT INTO Warehouse_fulfillment
("id", "order_id", "status")
VALUES
('8a69edde-2346-48b8-96d0-6c4e25527f38', '9cb99fd9-9e5e-4240-8162-d28747be01cd', 'FAILLED'),
('a2006a64-9bdc-4bfa-ba14-a44769aeb4a2', '9cb99fd9-9e5e-4240-8162-d28747be01cd', 'REJECTED'),
('bf0aa1fc-6dfc-4fd0-ba20-be101b1985d1', '9cb99fd9-9e5e-4240-8162-d28747be01cd', 'FAILED'),
('48c7d747-2f9b-4535-8f27-210a43cf5c30', '9cb99fd9-9e5e-4240-8162-d28747be01cd', 'SUCCESS'),
('7f8e18c9-4322-428a-9370-9ecd1c5ef286', '13370467-cf0c-47f2-8fea-a215500607e6', 'FAILED')
;
Query 1:
select
o.*, w.name, s.status, s.rn
from Warehouse_order o
left join Warehouse w on o.Warehouse_id = w.id
left join (
select id, order_id, status
, row_number() over(partition by order_id
order by case when status = 'SUCCESS' then 1
when status = 'FAILED' then 2
when status = 'REJECTED' then 3
else 4 end) as rn
from Warehouse_fulfillment
) s on o.id = s.Order_id and rn=1
Results:
| id | warehouse_id | type | quantity | name | status | rn |
|--------------------------------------|--------------------------------------|--------|----------|--------|---------|--------|
| eceb0b5a-5afa-40e4-ac62-efb686e3bdae | 9bcae08e-ad36-4d97-b9ec-4857714e902a | BN_200 | 400 | big | (null) | (null) |
| 9cb99fd9-9e5e-4240-8162-d28747be01cd | b442e783-4725-41e9-af83-f75004ee1b38 | BN_100 | 100 | bigger | SUCCESS | 1 |
| 13370467-cf0c-47f2-8fea-a215500607e6 | 986d5aa9-0523-42d8-b183-dfd546d3e68 | BN_300 | 10 | (null) | FAILED | 1 |
I'm not entirely sure on this, but it sounds like you just want a left join on the order_fulfillment table with the "success" condition in the join:
select
w.id, w.name, w.location,
o.id as order_id, o.type as order_type,
o.quantity as order_quantity,
f.id as fulfillment_id
from
warehouse w
join warehouse_order o on
w.id = o.warehouse_id
left join warhouse_fulfillment f on
o.id = f.order_id and
f.status = 'SUCCESS'
Since you don't seem to care about non-successful records, and there is guarantee that there will only be one 'SUCCESS' fulfillment record, this should avoid any duplicates.

How to return only latest record on join [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 7 years ago.
I'm joining tables. I only want to return one record from the joining table, based on a date field.
Here's a simplified fiddle of what I've done so far: http://sqlfiddle.com/#!3/be0cdd/2
My tables:
CUSTOMER
| CustomerID |
--------------
| 1 |
PURCHASE
| PurchaseID | CustomerID | ProductID | CreateDate | ArchiveFlag |
------------------------------------------------------------------
| 1 | 1 | 443 | 01-FEB-15 | F |
| 2 | 1 | 551 | 01-MAR-15 | F |
| 3 | 1 | 151 | 01-JAN-15 | F |
| 4 | 1 | 654 | 01-MAY-15 | T |
| 5 | 1 | 345 | 01-APR-15 | T |
and here's the query itself:
select *
from customer c
join purchase p
on c.customerid = p.customerid
and p.archiveflag = 'F';
I only want to return the latest purchase that isn't archived (in this example, purchase ID 2) for each customer.
Ideal output:
| CustomerID | PurchaseID | CustomerID_2 | ProductID | CreateDate | ArchiveFlag |
|--------------------------------------------------------------------------------
| 1 | 2 | 1 | 551 | 01-MAR-15 | F |
Oracle 12c introduced a row limiting clause and you could do (if you only want a single result):
SELECT *
FROM customer c
INNER JOIN purchase p
ON ( c.customerid = p.customerid )
WHERE p.archiveflag = 'F'
ORDER BY
CreateDate DESC
FETCH FIRST 1 ROW ONLY
In earlier versions you can do:
SQL Fiddle
Oracle 11g R2 Schema Setup:
create table CUSTOMER(CustomerID INT);
create table PURCHASE(PurchaseID INT, CustomerID INT, ProductID INT, CreateDate date, ArchiveFlag char);
insert into CUSTOMER values(1);
insert into CUSTOMER values(2);
insert into PURCHASE values(1,1,443,'01-FEB-15','F');
insert into PURCHASE values(2,1,551,'01-MAR-15','F');
insert into PURCHASE values(3,1,151,'01-JAN-15','F');
insert into PURCHASE values(4,1,654,'01-MAY-15','T');
insert into PURCHASE values(5,1,345,'01-APR-15','T');
insert into PURCHASE values(6,2,234,'01-MAY-15','T');
insert into PURCHASE values(7,2,134,'01-APR-15','F');
insert into PURCHASE values(8,2,999,'01-JAN-15','F');
insert into PURCHASE values(9,2,724,'07-JUN-15','F');
insert into PURCHASE values(10,2,345,'01-JUN-15','T');
Query 1 - If you only want to get the latest for a single customer:
SELECT *
FROM (
SELECT *
FROM Purchase
WHERE archiveflag = 'F'
AND CustomerID = 1
ORDER BY
CreateDate DESC
)
WHERE ROWNUM = 1
Results:
| PURCHASEID | CUSTOMERID | PRODUCTID | CREATEDATE | ARCHIVEFLAG |
|------------|------------|-----------|-------------------------|-------------|
| 2 | 1 | 551 | March, 01 2015 00:00:00 | F |
Query 2 - If you want to get the latest for all customers:
SELECT PurchaseID,
CustomerID,
ProductID,
CreateDate,
ArchiveFlag
FROM (
SELECT p.*,
ROW_NUMBER() OVER ( PARTITION BY p.CustomerID ORDER BY CreateDate DESC ) RN
FROM purchase p
WHERE ArchiveFlag = 'F'
)
WHERE RN = 1
Results:
| PURCHASEID | CUSTOMERID | PRODUCTID | CREATEDATE | ARCHIVEFLAG |
|------------|------------|-----------|-------------------------|-------------|
| 2 | 1 | 551 | March, 01 2015 00:00:00 | F |
| 9 | 2 | 724 | June, 07 2015 00:00:00 | F |
If PURCHASE.CUSTOMERID is a not null foreign key linked to CUSTOMER.CUSTOMERID then you do not need to join the tables (as above).
I think you want to use row_number():
select *
from customer c join
(select p.*,
row_number() over (partition by p.customerid order by p.createdate desc) as seqnum
from purchase p
where p.archiveflag = 'F'
) p
on c.customerid = p.customerid and seqnum = 1;
SQL Fiddle
Schema setup
create table CUSTOMER(CustomerID int)
create table PURCHASE(PurchaseID int, CustomerID int, ProductID int, CreateDate date, ArchiveFlag char)
insert into CUSTOMER values(1)
insert into CUSTOMER values(2)
insert into PURCHASE values(1,1,443,'01-FEB-15','F')
insert into PURCHASE values(2,1,551,'01-MAR-15','F')
insert into PURCHASE values(3,1,151,'01-JAN-15','F')
insert into PURCHASE values(4,1,654,'01-MAY-15','T')
insert into PURCHASE values(5,1,345,'01-APR-15','T')
insert into PURCHASE values(6,2,331,'01-FEB-15','T')
insert into PURCHASE values(7,2,298,'01-JUN-15','F')
Query to get latest pending for all customers
select *
from purchase pa join customer c on c.customerid=pa.customerid
where pa.archiveflag = 'F'
and pa.createdate=(select max(createdate)
from purchase pb
where pa.customerid=pb.customerid
and pb.archiveflag='F')
Output
| PurchaseID | CustomerID | ProductID | CreateDate | ArchiveFlag | CustomerID |
|------------|------------|-----------|------------|-------------|------------|
| 2 | 1 | 551 | 2015-03-01 | F | 1 |
| 7 | 2 | 298 | 2015-06-01 | F | 2 |
You can use top and order by in your query like this
select Top 1 *
from customer c
join purchase p
on c.customerid = p.customerid
and p.archiveflag = 'F'
Order by p.CreateDate Desc;
Just use where clause: as told you wanted purchase ID 2: do as below:
SELECT * FROM
select *
from customer c
join purchase p
on c.customerid = p.customerid
and p.archiveflag = 'F';
order by CreateDate desc
where PurchasedID = 2;
Try this one..
select top 1 *
from customer c
join purchase p
on c.customerid = p.customerid
and p.archiveflag = 'F'
order by CreateDate desc;

Sql Inner Join among 2 tables summing the qty field multiple times

I have two tables , A and B
Table A contains:
OrderNo | StyleNo | Qty
O-20 | S-15 | 20
O-20 | S-18 | 40
O-25 | S-19 | 50
Table B contains:
OrderNo | StyleNo | Ship Qty
O-20 | S-15 | 5
O-20 | S-18 | 30
O-20 | S-15 | 12
O-20 | S-18 | 6
Result Requires
OrderNo | StyleNo | Qty | Ship Qty
O-20 | S-15 | 20 | 17
O-20 | S-18 | 40 | 36
O-25 | S-19 | 50 | 0
The following query is not working
select
B.Orderno, B.StyleNo, sum(A.Qty), sum(B.QtyShip)
from
A
inner join
B on A.OrderNo = B.OrderNo and A.StyleNo = B.StyleNo
group by
B.OrderNo, B.StyleNo
The issue you're having is that it's summing the qty field multiple times. Move the sums to subqueries and use a join on those:
select a.orderno, a.styleno, a.qty, b.qtyship
from (
select orderno, styleno, sum(qty) qty
from a
group by orderno, styleno
) a
join (
select orderno, styleno, sum(qtyship) qtyship
from b
group by orderno, styleno
) b on a.orderno = b.orderno and a.styleno = b.styleno
SQL Fiddle Demo