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

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.

Related

SQL Server : return most recent record based on date in column

I'm having an issue with a pretty basic query, my temp table has a primary key and a column named PropertyID1.
I'm joining to another table that has 3 columns:
PropertyID, SalePrice, SaleDate
The query joins on PropertyID1 and PropertyID, what I'm trying to get is the most recent iteration of the matching PropertyID as there can be 10+ matches per ID and I need the most recent only, with the column SaleDate being used to select the required records.
So the initial query is
CREATE TABLE ##RPP_CHECK
(
ID INT IDENTITY(1,1) PRIMARY KEY,
PropertyID1 VARCHAR(255) NULL
);
INSERT INTO ##IDCHECK
VALUES (41572498), (41484495), (41590235), (41611406)
SELECT
ID, ##IDCHECK.PropertyID1, PropertyID, SalePrice, SaleDate
FROM
##IDCHECK
LEFT JOIN
[ODS].[RS1] ON [ODS].[RS1].[PropertyID] = ##IDCHECK.PropertyID1
ORDER BY
ID
Which returns
ID PropertyID1 PropertyID SalePrice SaleDate
--------------------------------------------------
1 41572498 41572498 0.0 01-01-2011
1 41572498 41572498 0.0 01-01-2012
1 41572498 41572498 1000 01-01-2018
2 41484495 41484495 1200 01-02-2018
3 41590235 41590235 2000 01-03-2018
3 41590235 41590235 0.0 01-01-1999
4 41611406 41611406 5000 01-10-2018
What I need it to return is
ID PropertyID1 PropertyID SalePrice SaleDate
------------------------------------------------
1 41572498 41572498 1000 01-01-2018
2 41484495 41484495 1200 01-02-2018
3 41590235 41590235 2000 01-03-2018
4 41611406 41611406 5000 01-10-2018
I tried looking at some of the answers posted here, but they don't quite seem to fit, I'm pretty sure this is either a MAX( issue, or grouping, but not 100% sure so happy for any tips you can offer.
Cheers
APPLY often has the best performance:
SELECT ic.*, r.*
FROM ##IDCHECK ic OUTER APPLY
(SELECT TOP (1) r.*
FROM [ODS].[RS1] r
WHERE r.[PropertyID] = ic.PropertyID1
ORDER BY r.SaleDate DESC
) r;
One option uses ROW_NUMBER:
SELECT ID, PropertyID1, PropertyID, SalePrice, SaleDate
FROM
(
SELECT ID, t1.PropertyID1, PropertyID, SalePrice, SaleDate,
ROW_NUMBER() OVER (PARTITION BY PropertyID ORDER BY SaleDate DESC) rn
FROM IDCHECK t1
LEFT JOIN [ODS].[RS1] t2
ON t2.PropertyID = t1.PropertyID1
) t
WHERE rn = 1;
Use MAX built in function and GROUPBY built in function in JOIN and achieve your
result
BEGIN TRAN
CREATE TABLE #Test ( ID INT , PropertyID1 VARCHAR(100) , PropertyID
VARCHAR(100) , SalePrice DECIMAL(12,2) , SaleDate DATETIME )
INSERT INTO #Test ( ID , PropertyID1 , PropertyID , SalePrice , SaleDate)
SELECT 1,'41572498','41572498',0.0,CONVERT(DATETIME,'01-01-2011',103) UNION ALL
SELECT 1,'41572498','41572498',0.0,CONVERT(DATETIME,'01-01-2012',103) UNION ALL
SELECT 1,'41572498','41572498',1000,CONVERT(DATETIME,'01-01-2018',103) UNION ALL
SELECT 2,'41484495','41484495',1200,CONVERT(DATETIME,'01-02-2018',103) UNION ALL
SELECT 3,'41590235','41590235',2000,CONVERT(DATETIME,'01-03-2018',103) UNION ALL
SELECT 3,'41590235','41590235',0.0,CONVERT(DATETIME,'01-01-1999',103) UNION ALL
SELECT 4,'41611406','41611406',5000,CONVERT(DATETIME,'01-10-2018',103)
SELECT ID , PropertyID1 , PropertyID , SalePrice , SaleDate
FROM #Test
JOIN
(
SELECT PropertyID _PropertyID , PropertyID1 _PropertyID1 , MAX(SaleDate)
_SaleDate
FROM #Test
GROUP BY PropertyID,PropertyID1
) A ON _SaleDate = SaleDate
ROLLBACK TRAN

SQL Server 2008 R2: Pivot table query performance

Table: Product
create table Product
(
productID int,
productName varchar(20),
productsalesdate DATETIME,
producttype varchar(20)
);
Insertion:
insert into product values(1,'PenDrive','2010-01-01','Electronic');
insert into product values(1,'Computer','2016-01-01','Electronic');
insert into product values(1,'Laptop','2011-02-02','Electronic');
insert into product values(2,'textbook','2014-02-02','books');
insert into product values(2,'notebook','2016-01-01','books');
insert into product values(3,'Car','2016-01-01','Vehicle');
insert into product values(3,'Bike','2016-01-07','Vehicle');
First Try: In this I am getting wrong sum of productType
SELECT productID, FirstSale,LastSale, [Electronic],[books],[Vehicle]
FROM
(
SELECT
productID,
MIN(ProductSalesdate) as FirstSale,
MAX(ProductSalesdate) as LastSale,
productType
FROM
Product
Group by productID,productType
) a
PIVOT
(
COUNT(productType)
FOR productType IN ( [Electronic],[books],[Vehicle] )
) AS pvt;
Second Try: In this try I have solved the sum problem but the query is taking more time for execute for huge records.
SELECT productID,FirstSale,LastSale ,[Electronic],[books],[Vehicle]
FROM
(
SELECT a.ProductID, a.FirstSale, a.LastSale, b.ProductType
FROM Product b
inner join
(
SELECT
productID,
MIN(ProductSalesdate) as FirstSale,
MAX(ProductSalesdate) as LastSale
FROM
Product
Group by productID
) as a
ON a.ProductID = b.ProductID
) ab
PIVOT
(
COUNT(productType)
FOR productType IN ( [Electronic],[books],[Vehicle] )
) AS pvt;
Note: The second query is works fine but the problem is with the performance, because of
I am joining two same table because to get count of productType in the pivot query.
Question: How to optimize the second query which is a my second try?
The following uses a temporary table to store the derived table ab. My guess is it will improve the execution plan of the second query.
SELECT a.ProductID, a.FirstSale, a.LastSale, b.ProductType
INTO #ab
FROM Product b
inner join
(
SELECT
productID,
MIN(ProductSalesdate) as FirstSale,
MAX(ProductSalesdate) as LastSale
FROM
Product
Group by productID
) as a
ON a.ProductID = b.ProductID;
SELECT productID,FirstSale,LastSale ,[Electronic],[books],[Vehicle]
FROM #ab AS ab
PIVOT
(
COUNT(productType)
FOR productType IN ( [Electronic],[books],[Vehicle] )
) AS pvt;
DROP TABLE #ab;
EDIT: Just for sports I wrote following script which has 15k rows in #product. The whole script executes in ~1 second. I still don't understand how your query takes 5.5 minutes. Here goes:
SET NOCOUNT ON;
CREATE TABLE #product (
product_id INT,
product_name VARCHAR(20),
product_sales_date DATE,
product_type VARCHAR(20)
);
DECLARE #cnt INT=0;
WHILE #cnt<15000
BEGIN
INSERT INTO #product(
product_id,
product_name,
product_sales_date,
product_type
)
SELECT
product_id=ROUND(20*RAND(),0),
product_name=LEFT(NEWID(),20),
product_sales_date=DATEADD(DAY,ROUND((-10+20*RAND()), 0),GETDATE()),
product_type=
CASE ROUND(2*RAND(),0)
WHEN 0 THEN 'Electronic'
WHEN 1 THEN 'books'
ELSE 'Vehicle'
END;
SET #cnt=#cnt+1;
END
SELECT a.product_id, a.first_sale, a.last_sale, b.product_type
INTO #ab
FROM #product b
inner join
(
SELECT
product_id,
MIN(product_sales_date) as first_sale,
MAX(product_sales_date) as last_sale
FROM
#product
GROUP BY
product_id
) as a
ON a.product_id= b.product_id;
SELECT product_id,first_sale,last_sale,[Electronic],[books],[Vehicle]
FROM #ab AS ab
PIVOT
(
COUNT(product_type)
FOR product_type IN ( [Electronic],[books],[Vehicle] )
) AS pvt;
DROP TABLE #ab;
DROP TABLE #product;
Seems like you're trying to do something like this.. Not sure why you'd need extra joins or temp tables..
SELECT * FROM
(
SELECT productID,
productType,
MIN(ProductSalesdate) as FirstSale,
MAX(ProductSalesdate) as LastSale,
COUNT(productType) AS ProductCount
FROM Product
GROUP BY productID,productType
) t
PIVOT
(
SUM(ProductCount)
FOR productType IN ([Electronic],[books],[Vehicle])
) p
you'll get NULLS for the 0 counts but you can coalesce those values to 0 pretty easily

SQL Standard Value and Variations

Below is a sample of data
UnitID ITEM_Num Price
13446 71079 45.57
13447 71079 45.57
13448 71079 52.50
13449 71079 45.57
13450 71079 36.22
The actual dataset has roughly 100 unique UnitIDs and 700 unique Item_Num values. I am trying to determine the most common price for each Item_Num and then select any records that vary from that standard by more than a specified percent.
Ideally we would have a standard Price value for each item but we don't. What is the best way to find the most common value. Also is there a function that might be able to quickly rank the Items with the most variation is Price.
This is SQL Server 2012.
You can use GROUP BY statement:
SELECT Price, count(*) FROM my_table GROUP BY Price ORDER BY Price ASC
Hope this helps!
The following query should work in SQL Server. It should give back each ITEM_Num with a price 10% lower or higher than the most common price.
;WITH cte AS (
SELECT
RANK() OVER (PARTITION BY ITEM_Num ORDER BY COUNT(1) DESC) AS 'Rank'
, ITEM_Num
, Price
FROM Units
GROUP BY ITEM_Num, Price
)
SELECT u1.UnitID
, u1.ITEM_Num
, u1.Price
, u2.Price AS 'most common price'
FROM Units u1
INNER JOIN cte AS u2
ON u2.ITEM_Num = u1.ITEM_Num
AND u2.Rank = 1
WHERE ABS(u1.Price - u2.Price) >= (u2.Price * 0.1);
EDIT: I wrote the query not knowing your DBMS, could probably be more efficient using the ranking functions of SQL Server.
EDIT 2: http://sqlfiddle.com/#!6/74940/33
Create table #t(
UnitID int,
Item_Num int,
Price money
)
Insert into #t(Unitid, Item_Num, Price)
values(13446, 71079, 45.57 ),
(13447, 71079, 45.57),
(13448, 71079, 52.50),
(13449, 71079, 45.57),
(13450, 71079, 36.22)
;with cte as (
Select
Unitid, Item_Num, Price,
Row_Number() over ( partition by item_num order by price) rownum
from #t
)
Select
u.UnitID,
u.Item_Num,
u.Price,
U1.price as CommonPrice,
u.RowNum,
U.Price*0.1,
(u.price +(u.price*0.1)) as NewPrice
from cte as U
inner join #t u1 on u.item_num =u1.item_num
where u.rownum =1

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)

select latest 2 records of each product id in single table

this is my table structure,
create table ArticleTbl
(
ArticleID bigint identity(1,1),
ProductID int ,
ArticleName varchar(100),
PubDate datetime,
AuthorName varchar(50),
AuthorImage bit,
HtmlValues nvarchar(max)
)
here productid are
1=creditcard,2=prepaidcard,3 saving account,.........
each productid is having multiple rows of records ,
i want to select latest 2 records of each productid in one shot instead of going to database each time .
my procedure now is like..
create proc USP_GetArticle_ByProduct(#ProductID int) as
select top(2) * from ArticleTbl where ProductID=#ProductID
if i use this procedure each productid i have to go to database...
how to get one shot all product(latest 2 records ) using query????
SELECT
*
FROM
(
SELECT
/*Random order per product*/
ROW_NUMBER() OVER (PARTITION BY ProductID ORDER BY NEWID() ) AS Ranking,
*
FROM
ArticleTbl
) foo
WHERE
foo.Ranking <= 2
i figure this is on sql server yeah?
if so, you could do this...
select a1.*
from Articletbl a1
where a1.articleid in
(select top 2 a2.articleid
from ArticleTbl a2
where a2.productid = a1.productid
order by a2.articleid DESC)
order by a1.ProductID