display latest bill month - sql

I have t_cust table and I want to display customer's latest bill month and bill amount.
I have written query but for large data it will take much time.Is there any simple
logic to find latest bill month?
create table t_cust
(
cust_id varchar2(3),
b_year varchar2(4),
b_month varchar2(2),
b_amount number
);
insert into t_cust values('ABC','2015','11',100);
insert into t_cust values('ABC','2015','12',200);
insert into t_cust values('ABC','2016','01',300);
insert into t_cust values('XYZ','2016','01',1000);
insert into t_cust values('XYZ','2016','02',2000);
insert into t_cust values('XYZ','2016','03',3000);
commit;
select cust_id,substr(to_date(b_year*100+b_month,'YYYYMM'),4)latest_bill_mth,b_amount bill_amount
from t_cust
where (cust_id,b_year*100+b_month) in (
select cust_id,max(b_year*100+b_month)
from t_cust
group by cust_id
);

Looks like Oracle, so simply use a ROW_NUMBER/RANK:
select ...
from
(
select ...
,row_number()
over (partition by t_cust
order by year desc, month desc) as rn
) dt
where rn = 1

Related

How to query latest records in SQL Server

I've tried to to use similar asks but still not able to get my desired result. Here is three sample records.
create table #temps
(
airport varchar(10),
country varchar(10),
plane varchar(10),
id int,
flight_date datetime
)
insert into #temps
values ('IAD', 'USA', 'a777', '195', ' 7/26/2022 11:39:00 AM')
insert into #temps
values ('IAD', 'USA', 'a777', '195', ' 8/12/2022 9:51:00 AM')
insert into #temps
values ('BOS', 'USA', 'a777', '195', ' 8/12/2022 9:51:00 AM')
I tried to retrieve the latest record which is from BOS airport (discard the impossible of the same flight dates occurred from different airports)
I used the ROW_NUMBER such as below and and wanted to return the max rank = 3.
SELECT DISTINCT
a.airport, a.country, a.flight_date, a.plane, id,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY flight_date ASC) AS Ct
FROM
#temps a
I also tried the max such as
SELECT A.airport, A.id, A.flight_date, A.country
FROM #temps A
INNER JOIN (SELECT id, MAX(flight_date) as MAX_FLIGHT_DATE
FROM #temps
GROUP BY id) B ON (A.flight_date = B.MAX_FLIGHT_DATE)
Is there a better technique that can return the record from BOS airport?
Thanks!
joe
You could use ORDER by.
SELECT TOP(3) airport,
country,
plane,
id,
flight_date
FROM #temps
ORDER BY flight_date DESC;

How do I find the account having the maximum balance in each branch of a bank using SQL?

How do I find the account having the maximum balance in each branch of a bank using SQL? I have a table having 3 columns account number, balance and branch.
If you are using SQL server the following will work using ROW_NUMBER():
CREATE TABLE #TEMP_A
(
BALANCE INT,
BRANCH VARCHAR(20),
ACCOUNT INT
)
INSERT INTO #TEMP_A
VALUES(100,'WEST',001)
INSERT INTO #TEMP_A
VALUES(200,'WEST',002)
INSERT INTO #TEMP_A
VALUES(400,'EAST',003)
INSERT INTO #TEMP_A
VALUES(500,'EAST',004)
SELECT ACCOUNT FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY BRANCH ORDER BY BALANCE DESC) AS ROWNUM,* FROM #TEMP_A
) AS X WHERE ROWNUM = 1
---------+---------+---------+---------+---------+---------+---------+
SELECT DISTINCT(A.ACCOUNT), TMP.BRANCH, TMP.AMT
FROM TABLE A,
( SELECT BRANCH, MAX(AMOUNT) AS AMT
FROM TABLE
GROUP BY BRANCH) AS TMP
WHERE A.BRANCH = TMP.BRANCH
AND A.AMOUNT = TMP.AMT
GROUP BY A.ACCOUNT, TMP.BRANCH, TMP.AMT ;
---------+---------+---------+---------+---------+---------+---------+

Join two tables and get data between date range using two tables

I created two tables
Item_In
Item_Out
with this code:
CREATE TABLE Item_In
(
Id INT PRIMARY KEY,
ItemId INT,
ItemCode VARCHAR(40),
Name VARCHAR(40),
Price MONEY,
Quanity INT,
InDate DATE
)
CREATE TABLE Item_Out
(
Id INT PRIMARY KEY,
ItemId INT,
ItemCode VARCHAR(40),
Name VARCHAR(40),
SellingPrice MONEY,
Quanity INT,
OutDate DATE
)
Sample data:
insert into Item_In values('0001','C01','LAPTOP','75000','10','2015/05/10')
insert into Item_In values('0002','E01','HD','8000','8','2015/05/10')
insert into Item_In values('0003','MO1','RAM','5000','9','2015/06/10')
insert into Item_In values('0004','W01','MOUSE','250','12','2015/05/10')
insert into Item_In values('0001','C01','LAPTOP','75000','9','2015/06/25')
insert into Item_In values('0001','C01','LAPTOP','75000','8','2015/04/10')
insert into Item_In values('0002','E01','HD','8000','6','2015/08/10')
insert into Item_Out values('0002','E01','HD','8000','2','2015/06/18')
insert into Item_Out values('0001','C01','LAPTOP','75000','2','2015/05/20')
insert into Item_Out values('0002','E01','HD','8000','3','2015/05/11')
insert into Item_Out values('0003','MO1','RAM','5000','2','2015/06/15')
insert into Item_Out values('0004','W01','MOUSE','250','1','2015/05/16')
insert into Item_Out values('0001','C01','LAPTOP','75000','3','2015/06/29')
insert into Item_Out values('0001','C01','LAPTOP','75000','1','2015/04/19')
ID is an auto-increment column.
I want to join this two tables and get data between date range, with sum of Quantity and Balance Quantity
I hope to get following out put,and I used date range as 2015/04/01 and 2015/06/30. In bellow I typed only two records,
ItemId | ItemCode | QuantityIN | QuantityOut | BalanceQuantity
-------+----------+------------+-------------+---------------
001 |C01 |27 |6 |21
002 |E01 |8 |5 |3
I try to do it using bellow code,But it not working as I wish.
SELECT
dbo.Item_In.ItemId, dbo.Item_In.ItemCode,
SUM(dbo.Item_In.Quanity) AS Expr2,
SUM(dbo.Itenm_Out.Quanity) AS Expr1
FROM
dbo.Item_In
INNER JOIN
dbo.Itenm_Out ON dbo.Item_In.Id = dbo.Itenm_Out.Id
GROUP BY
dbo.Item_In.ItemId, dbo.Item_In.ItemCode
ORDER BY
dbo.Item_In.ItemId
Give this a try
SELECT max(dbo.Item_In.ItemId) as 'Item_ID', max(dbo.Item_In.ItemCode) as 'Item_Code', SUM(dbo.Item_In.Quanity) AS QtyIn, SUM(dbo.Item_Out.Quanity) AS QtyOut,(SUM(dbo.Item_In.Quanity) - SUM(dbo.Item_Out.Quanity)) as Balance
FROM dbo.Item_In INNER JOIN dbo.Itenm_Out ON dbo.Item_In.Id = dbo.Itenm_Out.Id
where indate between '2015-04-01' and '2015-06-30' and outdate between '2015/04/01' and '2015/06/30'
GROUP BY dbo.Item_In.ItemId, dbo.Item_In.ItemCode
ORDER BY dbo.Item_In.ItemId
This is harder than it seems. You need to add up the inventory from the two tables, combining them to avoid duplicates and count everything. So, the following gives the sum on each date:
select ItemId, ItemCode, thedate, sum(inq) as inq, sum(outq) as outq
from ((select i.ItemId, i.ItemCode, i.inDate as thedate, i.Quantity as inq, 0 as outq
from item_in i
) union all
(select o.ItemId, o.ItemCode, o.inDate as thedate, 0, - o.Quantity
from item_in i
)
) io;
However, your final answer requires cumulative sums. In SQL Server 2012+, you would do:
select ItemId, ItemCode, thedate,
sum(sum(inq)) over (partition by ItemId order by thedate) as inq,
sum(sum(outq)) over (partition by ItemId order by thedate) as outq,
(sum(sum(inq)) over (partition by ItemId order by thedate) -
sum(sum(outq)) over (partition by ItemId order by thedate)
) as balance
from ((select i.ItemId, i.ItemCode, i.inDate as thedate, i.Quantity as inq, 0 as outq
from item_in i
) union all
(select o.ItemId, o.ItemCode, o.inDate as thedate, 0, o.Quantity
from item_in i
)
) io;
In earlier versions of SQL Server, you would have to take a different approach.

Oracle SQL : table Joining

For Oracle Database, suppose I have two tables here (Similar structure, but much larger amount of data) Definition below:
create table payments(
record_no INTEGER;
cust_no INTEGER;
amount NUMBER;
date_entered DATE;
);
insert into payments values(1,3,34.5,sysdate-1);
insert into payments values(2,2,34.5,sysdate-2);
insert into payments values(3,3,34.5,sysdate-18/1440);
insert into payments values(4,1,34.5,sysdate-1);
insert into payments values(5,2,34.5,sysdate-2/24);
insert into payments values(6,3,34.5,sysdate-56/1440);
insert into payments values(7,4,34.5,sysdate-2);
insert into payments values(8,2,34.5,sysdate-1);
create table customer(
cust_no INTEGER;
name VARCHAR2;
zip VARCHAR2;
);
insert into customer values(1,'Tom',90001);
insert into customer values(2,'Bob',90001);
insert into customer values(3,'Jack',90001);
insert into customer values(4,'Jay',90001);
Now I want to generate a report with those columns (Get the first two payment amount and date for each customer order by paydate) :
Cust_no | pay_amount1 | pay_date1 |pay_amount2 | pay_date2
Sample report I want
CUST_NO PAYMENT1 PAYDATE1 PAYMENT2 PAYDATE2
1 34.5 October, 09 2013 0 null
2 34.5 October, 08 2013 34.5 October, 09 2013
3 34.5 October, 09 2013 34.5 October, 10 2013
4 34.5 October, 08 2013 0 null
Can anybody make a correct and efficient Query ? Thanks ahead.
First you need to get your creation script right. The ; terminates a statement not a line inside a statement. Secondly the varchar2 data types needs a length specification: name VARCHAR2(20) instead of name VARCHAR2. Also character literals need to be enclosed in single quotes. '90001' is a character literal, 90001 is a number. Those are two different things.
So this results in the following script:
create table payments(
record_no INTEGER,
cust_no INTEGER,
amount NUMBER,
date_entered DATE
);
insert into payments values(1,3,34.5,sysdate-1);
insert into payments values(2,2,34.5,sysdate-2);
insert into payments values(3,3,34.5,sysdate-18/1440);
insert into payments values(4,1,34.5,sysdate-1);
insert into payments values(5,2,34.5,sysdate-2/24);
insert into payments values(6,3,34.5,sysdate-56/1440);
insert into payments values(7,4,34.5,sysdate-2);
insert into payments values(8,2,34.5,sysdate-1);
create table customer(
cust_no INTEGER,
name VARCHAR2(20),
zip VARCHAR2(20)
);
insert into customer values(1,'Tom','90001');
insert into customer values(2,'Bob','90001');
insert into customer values(3,'Jack','90001');
insert into customer values(4,'Jay','90001');
Note that it's bad coding practice to not specify the columns in an INSERT statement. It should be insert into customer (cust_no, name, zip) values(1,'Tom','90001'); instead of insert into customer values(1,'Tom','90001');
Now for your query, the following should do you wnat you need:
with numbered_payments as (
select cust_no,
amount,
date_entered,
row_number() over (partition by cust_no order by date_entered) as rn
from payments
)
select c.cust_no,
c.name,
p1.amount as pay_amount1,
p1.date_entered as pay_date1,
p2.amount as pay_amount2,
p2.date_entered as pay_date2
from customer c
left join numbered_payments p1
on p1.cust_no = c.cust_no
and p1.rn = 1
left join numbered_payments p2
on p2.cust_no = c.cust_no
and p2.rn = 2;
Note that I used an outer join to ensure that every customer is returned even if there is no or only a single payment for it.
Here is an SQLFiddle with all corrections and the query: http://sqlfiddle.com/#!4/74349/3
SELECT * FROM (
SELECT
c.cust_no,
p.amount as payment,
p.date_entered as paydate,
ROW_NUMBER() OVER (PARTITION BY cust_no ORDER BY p.record_no ASC) AS rn
FROM customer c
JOIN payments p ON p.cust_no = c.cust_no
) t
WHERE
rn <= 2
ORDER BY cust_no, rn;
Will show the 2 records per client you need, in 2 separate lines. If you prefer having it in the same line, then use this query:
SELECT
cust_no,
payment1,
paydate1,
CASE WHEN nextcli <> cust_no THEN 0 ELSE payment2 END AS payment2,
CASE WHEN nextcli <> cust_no THEN SYSDATE ELSE paydate2 END AS paydate2
FROM (
SELECT
c.cust_no,
p.amount as payment1,
p.date_entered as paydate1,
ROW_NUMBER() OVER (PARTITION BY c.cust_no ORDER BY p.record_no ASC) AS rn,
LEAD(c.cust_no, 1, -1) OVER (ORDER BY c.cust_no ASC) as nextcli,
LEAD(p.amount, 1, 0) OVER (ORDER BY c.cust_no ASC) as payment2,
LEAD(p.date_entered, 1, NULL) OVER (ORDER BY c.cust_no ASC) as paydate2
FROM customer c
JOIN payments p ON p.cust_no = c.cust_no
) t
WHERE
rn <= 1
ORDER BY cust_no, rn;
The analytic function ROW_NUMBER can help you give a number to each payment :
select cust_no, amount, date_entered,
row_number() over(partition by cust_no order by date_entered ) rn
from payments ;
Using this I think we can get what you're looking for, something like :
With ordered_payments as (
select cust_no, amount, date_entered,
row_number() over(partition by cust_no order by date_entered ) rn
from payments)
select customer.cust_no, p1.amount, p1.date_entered, p2.amount, p2.date_entered
from customer left join ordered_payments p1
on customer.cust_no = p1.cust_no and p1.rn = 1
left join ordered_payments p2
on customer.cust_no = p2.cust_no and p2.rn = 2 ;

PostgreSQL SELECT the last order per customer per date range

In PostgreSQL:
I have a Table that has 3 columns:
CustomerNum, OrderNum, OrderDate.
There may(or may not) be many orders for each customer per date range. What I am needing is the last OrderNum for each Customer that lies in the date range that is supplied.
What I have been doing is getting a ResultSet of the customers and querying each one separately, but this is taking too much time.
Is there any way of using a sub-select to select out the customers, then get the last OrderNum for each Customer?
On postgres you can also use the non-standard DISTINCT ON clause:
SELECT DISTINCT ON (CustomerNum) CustomerNum, OrderNum, OrderDate
FROM Orders
WHERE OrderDate BETWEEN 'yesterday' AND 'today'
ORDER BY CustomerNum, OrderDate DESC;
See http://www.postgresql.org/docs/current/static/sql-select.html#SQL-DISTINCT
select customernum, max(ordernum)
from table
where orderdate between '...' and '...'
group by customernum
that's all.
SELECT t1.CustomerNum, t1.OrderNum As LastOrderNum, t1.LastOrderDate
FROM table1 As t1
WHERE t1.OrderDate = (SELECT MAX(t2.OrderDate)
FROM table1 t2
WHERE t1.CustomerNum = t2.CustomerNum
AND t2.OrderDate BETWEEN date1 AND date2)
AND t1.OrderDate BETWEEN date1 AND date2
Not sure about your Customer table's structure or relationships, but this should work:
SELECT Customer.Num, (
SELECT OrderNum FROM Orders WHERE CustomerNum = Customer.Num AND OrderDate BETWEEN :start AND :end ORDER BY OrderNum DESC LIMIT 1
) AS LastOrderNum
FROM Customer
If by last order number you mean the largest order number then you can just use your select as the predicate for customer num, group the results and select the maximum:
SELECT CustomerNum, MAX(OrderNum) AS LastOrderNum
FROM Orders
WHERE
CustomerNum IN (SELECT CustomerNum FROM ...)
AND
OrderDate BETWEEN :first_date AND :last_date
GROUP BY CustomerNum
If the last order number isn't necessarily the largest order number then you'll need to either find the largest order date for each customer and join it together with the rest of the orders to find the corresponding number(s):
SELECT O.CustomerNum, O.OrderNum AS LastOrderNum
FROM
(SELECT CustomerNum, MAX(OrderDate) AS OrderDate
FROM Orders
WHERE
OrderDate BETWEEN :first_date AND :last_date
AND
CustomerNum IN (SELECT CustomerNum FROM ...)
GROUP BY CustomerNum
) AS CustLatest
INNER JOIN
Orders AS O USING (CustomerNum, OrderDate);
-- generate some data
DROP TABLE tmp.orders;
CREATE TABLE tmp.orders
( id INTEGER NOT NULL
, odate DATE NOT NULL
, payload VARCHAR
)
;
ALTER TABLE tmp.orders ADD PRIMARY KEY (id,odate);
INSERT INTO tmp.orders(id,odate,payload) VALUES
(1, '2011-10-04' , 'one' )
, (1, '2011-10-24' , 'two' )
, (1, '2011-10-25' , 'three' )
, (1, '2011-10-26' , 'four' )
, (2, '2011-10-23' , 'five' )
, (2, '2011-10-24' , 'six' )
;
-- CTE to the rescue ...
WITH sel AS (
SELECT * FROM tmp.orders
WHERE odate BETWEEN '2011-10-23' AND '2011-10-24'
)
SELECT * FROM sel s0
WHERE NOT EXISTS (
SELECT * FROM sel sx
WHERE sx.id = s0.id
AND sx.odate > s0.odate
)
;
result:
DROP TABLE
CREATE TABLE
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "orders_pkey" for table "orders"
ALTER TABLE
INSERT 0 6
id | odate | payload
----+------------+---------
1 | 2011-10-24 | two
2 | 2011-10-24 | six
(2 rows)