Sql Select from two tables in single query - sql

I have Two Tables as under
SalesDetails PurchaseDetails
Date SaleOrderId ProductId Qty Date PurcsOrderId ProductId Qty
2/2/12 S_1 P_1 4 1/2/12 PO_1 P_1 50
3/2/12 S_2 P_1 5 4/2/12 PO_2 P_1 50
3/2/12 S_2 P_2 7
6/2/12 S_3 P_1 3
9/2/12 S_4 1 5
from these two tables i want to show a report like this
Product Inquiry Report
ProductId: P_1
Date TrId Debit Credit
1/2/12 PO_1 50
2/2/12 S_1 4
3/2/12 S_2 5
4/2/12 PO_2 50
6/2/12 S_3 3
Here Qty column of purchasedetails becomes 'Debit' in the report and Qty in SalesDetails becomes 'Credit' column in Report.In report, the transaction should be shown in order to Date as shown Is there a way to perform this in a single query, if yes, then how? if no, then is there any other solution? Thanks in advance

Use UNION:
SELECT Date, SaleOrderID as TrId, NULL as Debit, Qty as Credit
FROM SalesDetails
UNION
SELECT Date, PurcsOrderId AS TrId, Qty AS Debit, NULL as Credit
FROM PurchaseDetails
ORDER BY Date

SQL Fiddle:
SELECT *
FROM
(
SELECT sa.Date, sa.SaleOrderId AS TRId, Null AS Debit, sa.Qty AS Credit
FROM SalesDetails sa
UNION
SELECT pa.Date, pa.PurcsOrderId AS TRId, pa.Qty AS Debit, Null AS Credit
FROM PurchaseDetails pa
) m
ORDER BY 1
If you would rather the values be 0 then use 0 AS Debit and 0 AS Credit respectively.

Related

Get value based on date

I am trying to get the price point of a product based on future pricing. I can't really do it by Max (Expiration Date) since the price points are different. And I also can't do a Max(Price) since the high price might be the one expiring or it could be the new one. My data would look like this:
Supplier
Product
Price
Effective Date
Expiration Date
Supplier 1
A
800
04-01-2121
12-31-2023
Supplier 1
A
1000
01-01-2121
03-31-2023
Supplier 1
B
500
04-01-2121
12-31-2023
Supplier 1
B
400
01-01-2121
03-31-2023
Supplier 2
D
200
01-01-2121
12-31-2023
Supplier 2
C
600
01-01-2121
12-31-2023
The result I am trying to get is below:
Supplier
Product
Price
Effective Date
Expiration Date
Supplier 1
A
800
04-01-2121
12-31-2023
Supplier 1
B
500
04-01-2121
12-31-2023
Supplier 2
D
200
01-01-2121
12-31-2023
Supplier 2
C
600
01-01-2121
12-31-2023
Any ideas?
To have the product information when expirationdate is the greatest we can product wise group the rows in descending order of expirationdate and find the row with lowest serial number for each product or we can simply use subquery to select the information where expirationdate=max(expirationdate) within the product group.
First approach will be more efficient. But if your dbms doesn't support row_number() then you can use second approach.
Schema:
create table mydata(Supplier varchar(30),Product varchar(30), Price int,EffectiveDate date, ExpirationDate date);
insert into mydata values('Supplier 1', 'A', 800 ,'04-01-2121', '12-31-2023');
insert into mydata values('Supplier 1', 'A', 1000 ,'01-01-2121', '03-31-2023');
insert into mydata values('Supplier 1', 'B', 500 ,'04-01-2121', '12-31-2023');
insert into mydata values('Supplier 1', 'B', 400 ,'01-01-2121', '03-31-2023');
insert into mydata values('Supplier 2', 'D', 200 ,'01-01-2121', '12-31-2023');
insert into mydata values('Supplier 2', 'C', 600 ,'01-01-2121', '12-31-2023');
Query#1
WITH cte
AS (SELECT supplier,
product,
price,
effectivedate,
expirationdate,
ROW_NUMBER ()
OVER (PARTITION BY product
ORDER BY expirationdate DESC)
rn
FROM mydata)
SELECT supplier,
product,
price,
effectivedate,
expirationdate
FROM cte
WHERE rn = 1
Output:
supplier
product
price
effectivedate
expirationdate
Supplier 1
A
800
2121-04-01
2023-12-31
Supplier 1
B
500
2121-04-01
2023-12-31
Supplier 2
C
600
2121-01-01
2023-12-31
Supplier 2
D
200
2121-01-01
2023-12-31
Query#2 for older version of DBMS:
SELECT supplier,
product,
price,
effectivedate,
expirationdate
FROM mydata m
where effectivedate =
(select max(effectivedate) from mydata md where m.product=md.product)
Output:
supplier
product
price
effectivedate
expirationdate
Supplier 2
D
200
2121-01-01
2023-12-31
Supplier 2
C
600
2121-01-01
2023-12-31
Supplier 1
B
500
2121-04-01
2023-12-31
Supplier 1
A
800
2121-04-01
2023-12-31
db<>fiddle here
It looks you can accomplish what you need with a simple row_number by numbering the rows decending ordered against your ExpirationDate and filtering for the first row in each group.
select Supplier, Product, Price, EffectiveDate, ExpirationDate from (
select Supplier, Product, Price, EffectiveDate, ExpirationDate, row_number() over (partition by Product order by ExpirationDate desc)Seq
from table
)t
where Seq=1

Select From Multiple tables with group by in SQL

I have two views:
Purchase_Details view:
InvoiceId
ItemId
Quantity
Price
ExpireDate
ItemCode
StoreId
and Sale_Details view
InvoiceId
ItemId
Quantity
Price
ItemCode
StoreId
and Item Table:
ID
Name
SalePrice
PurchasePrice
Purchase_Detail View data
Id StoreId ItemCode Price Quantity ItemId ExpireDate
51 1 345 300.00 1.00 5 2019-10-25
52 1 348 300.00 22.00 5 2019-10-04
53 2 348 300.00 17.00 5 2019-11-11
54 1 345 300.00 8.00 5 2019-12-12
Sale_Details View data
Id StoreId ItemCode Price Quantity ItemId
55 1 345 300.00 4.00 5
56 1 348 300.00 3.00 5
I want to found sum of quantity of each item grouped by ItemId and ItemCode.
Second: is it better to put the sales and purchase and the same table or separate them (as I did?
I think you'll want to union the data together first, and then group by so your sum crosses the sets. You can do this with a CTE. Something like this:
WITH CTE_Detail AS (
SELECT ItemId, ItemCode, Quantity FROM Purchase_Details
UNION ALL
SELECT ItemId, ItemCode, Quantity FROM Sale_Details
)
SELECT ItemId, ItemCode, SUM(Quantity) AS Quantity
FROM CTE_Detail
GROUP BY ItemId, ItemCode
As far as your question about "what is better", that depends on how you want to use it. Obviously, there is a little additional complexity in this query by having them separate, but it might help you in other areas. I believe you said they are views, so can't you have it both ways?
This should do it
with purchases as (
select
ItemId,
ItemCode,
sum(quantity) as purchase_sum
from
Purchase_Detail
group by
1,
2
),
sales as (
select
ItemId,
ItemCode,
sum(quantity) as sales_sum
from
Sales_Details
group by
1,
2
)
select
ItemId,
ItemCode,
(
purchase_sum - sales_sum
) as net
from
purchases
join sales
using (
ItemId,
ItemCode
)

How to get the sum of a column value as a new column in the SQL result set

I have a table known as payment plans, I need to get the result set as in the below table.
CREATE TABLE PaymentPlans
(
PaymentPlanID int,
EmployeeID int,
PaidToDate money
)
INSERT INTO PaymentPlans VALUES (1,1,100)
INSERT INTO PaymentPlans VALUES (2,1,200)
INSERT INTO PaymentPlans VALUES (3,1,150)
In my select query, i need an extra column which provides me the sum of 'PaidToDate'. I'm trying with the below query, where i'm getting the result set for 'Total amount paid', same as 'Paid to date'.
SELECT PaymentPlanID, EmployeeID, PaidToDate, SUM(PaidToDate) AS 'TOTAL AMOUNT PAID' FROM PaymentPlans group by PaymentPlanID, EmployeeID, PaidToDate
SELECT a.PaymentPlanID, a.EmployeeID, a.PaidToDate, SUM(a.PaidToDate) AS
'TOTAL AMOUNT PAID' FROM (
SELECT PaymentPlanID, EmployeeID, PaidToDate FROM PaymentPlans
)a
GROUP BY a.PaymentPlanID, a.EmployeeID, a.PaidToDate
On both these scenarios, I'm getting the result as below,
PaymentPlanID EmployeeID PaidToDate TotalAmountPaid
1 1 100.00 100.00
2 1 200.00 200.00
3 1 150.00 150.00
Where as i need result as below,
PaymentPlanID EmployeeID PaidToDate TotalAmountPaid
1 1 100.00 450.00
2 1 200.00 450.00
3 1 150.00 450.00
Please let me know what am i missing in my query.
You can use the OVER clause. You might need to include the PARTITION BY clause, depending on your requirement.
SELECT PaymentPlanID,
EmployeeID,
PaidToDate,
SUM(PaidToDate) OVER () AS TotalAmountPaid
FROM dbo.PaymentPlans;
you can try below way
select *,(select sum(PaidToDate) from PaymentPlans) as total from PaymentPlans
PaymentPlanID EmployeeID PaidToDate total
1 1 100.00 450.00
2 1 200.00 450.00
3 1 150.00 450.00
In the case where you have multiple customer ids and you want each row to have that customer's total amount paid you would need to join to a sub query as follows
SELECT
a.PaymentPlanID,
a.EmployeeID,
a.PaidToDate,
b.TotalPaidToDate AS 'TOTAL AMOUNT PAID'
FROM PaymentPlans a
INNER JOIN (
SELECT
EmployeeID,
SUM(PaidToDate) AS TotalPaidToDate
FROM PaymentPlans
GROUP BY EmployeeID
) b
ON a.EmployeeID = b.EmployeeID
I would use Over clause that might be executed fast if the underlying records are more.
SELECT
PaymentPlanID,EmployeeID,
PaidToDate,SUM(PaidToDate) OVER () as total
FROM PaymentPlans
For complete the discution with Larnu,
method 1:
SELECT PaymentPlanID,
EmployeeID,
PaidToDate,
SUM(PaidToDate) OVER (partition by EmployeeID order by PaymentPlanID) AS TotalAmountPaidByEmployeeID,
SUM(PaidToDate) OVER () AS TotalAmountPaid
FROM dbo.PaymentPlans;
Method 2:
with Total as
(
select EmployeeID, sum(PaidToDate) TotalAmountPaidByEmployeeID
from dbo.PaymentPlans
group by EmployeeID
)
SELECT f1.*, f2.TotalAmountPaidByEmployeeID
FROM dbo.PaymentPlans f1 inner join Total f2 on f1.EmployeeID=f2.EmployeeID;
Method 3
SELECT f1.*, f3.TotalAmountPaidByEmployeeID
FROM dbo.PaymentPlans f1
cross apply
(
select sum(PaidToDate) TotalAmountPaidByEmployeeID from dbo.PaymentPlans f2
where f1.EmployeeID=f2.EmployeeID
) f3

SQL Running Total Reset on Condition

I have the following table:
Transaction History Table
TransactionHistoryId ProductCode Type Quantity PurchasePrice CurrentPrice
1 Product1 B 10 3.00 2.00
2 Product1 B 5 7.00 2.00
3 Product1 S -7 7.00 2.00
4 Product1 S -8 3.00 3.00
5 Product1 B 4 10.00 10.00
6 Product1 B 5 12.00 12.00
8 Product2 B 8 20.00 20.00
I would like to acheive the following table:
TransactionHistoryId ProductCode Type Quantity PurchasePrice QtyRunning PriceRunning
1 Product1 B 10 3.00 10 30.00
2 Product1 B 5 7.00 15 65.00
3 Product1 S -7 7.00 8 65.00
4 Product1 S -8 3.00 0 0.00
5 Product1 B 4 10.00 4 40.00
6 Product1 B 5 12.00 9 100.00
8 Product2 B 8 20.00 8 160.00
Create Table SQL
IF OBJECT_ID('TEMPDB..#TransactionHistory') IS NOT NULL
DROP TABLE #TransactionHistory
create table #TransactionHistory
(TransactionHistoryId int,
ProductCode varchar(10),
Type char(1),
Quantity smallint,
PurchasePrice decimal(18,2),
CurrentPrice decimal(18,2)
)
insert into #TransactionHistory
values
(1,'Product1','B',10,3.00,2.00),
(2,'Product1','B',5,7.00,2.00),
(3,'Product1','S',-7,7.00,2.00),
(4,'Product1','S',-8,3.00,3.00),
(5,'Product1','B',4,10.00,10.00),
(6,'Product1','B',5,12.00,12.00),
(8,'Product2','B',8,20.00,20.00)
Rules
PriceRunningTotal resets when the quantity running total is 0
PriceRunningTotal sums up only Type = 'B' (buys), when Type = 'S' (sold) keep the previous purchase price running total
Notice there is a Product 2 so it should have it's own running count independent of Product 1
Purpose
A query to ultimately find out the following:
Product Quantity AdjustedPurchasePrice
Product1 9 $11.11
Product2 8 $20
I used the following SQL Server 2012 query to get the result, but I feel it could be done much better:
Query
SELECT *,
PriceRunningTotalFinal =
SUM(CASE
WHEN QuantityRunningTotal = 0 THEN -1 * PriceRunningTotal
WHEN Quantity < 0 THEN 0 ELSE PurchasePrice * Quantity END) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
)
FROM (
SELECT TransactionHistoryId, ProductCode, Type, Quantity, PurchasePrice,
QuantityRunningTotal = SUM(Quantity) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
),
PriceRunningTotal = SUM(CASE WHEN Quantity < 0 THEN 0 ELSE PurchasePrice * Quantity END) OVER
(
PARTITION BY ProductCode
ORDER BY TransactionHistoryId ROWS UNBOUNDED PRECEDING
)
FROM TransactionHistory
) AS Results1
ORDER BY ProductCode;
Problem
Ideally I would of liked to use the QuantityRunningTotal within another query but I can't nest windowed functions.
Anyone know of a more efficient way to achieve this result?
Hmmm. I think something like this:
select th.*,
sum(case when type = 'B' then Quantity * PurchasePrice
else 0
end) over (partition by grp, ProductCode order by TransactionHistoryId
) as PriceRunningTotal
from (select th.*,
sum(case when running_quantity = 0 then 1 else 0 end) over (partition by ProductCode order by TransactionHistoryId) as grp
from (select th.*,
sum(quantity) over (partition by ProductCode order by TransactionHistoryId
) as running_quantity
from TransactionHistory th
) th;
I'm not sure if this is the same logic as your query. For this query:
The innermost subquery calculates the running quantity.
The middle subquery calculates a group based on the number of times the running quantity has been 0.
The outermost query then calculates the running price.

Calculation of balance after each transaction

I have table like this:
cust_id acc_no trans_id trans_type amount
1111 1001 10 credit 2000.0
1111 1001 11 credit 1000.0
1111 1001 12 debit 1000.0
2222 1002 13 credit 2000.0
2222 1002 14 debit 1000.0
I want a Hive query or sql query for every transaction done by a customer the balance should be calculated so.
I want output as follows:
cust_id acc_no trans_id trans_type amount balance
1111.0 1001.0 10.0 credit 2000.0 2000.0
1111.0 1001.0 11.0 credit 1000.0 3000.0
1111.0 1001.0 12.0 debit 1000.0 2000.0
2222.0 1002.0 13.0 credit 2000.0 2000.0
2222.0 1002.0 14.0 debit 1000.0 1000.0
I've tried
SELECT *
FROM (SELECT cust_id,
acc_no,
trans_id,
trans_type,
amount,
CASE
WHEN Trim(trans_type) = 'credit' THEN ball =
Trim(bal) + Trim(amt)
ELSE ball = Trim(bal) - Trim(amt)
end
FROM ban) l;
This query will do the trick :
SELECT t1.cust_id,t1.acc_no,t1.trans_id,t1.trans_type,t1.amount,
sum(t2.amount*case when t2.trans_type = 'credit' then 1
else -1 end) as balance
FROM Table1 t1
INNER JOIN Table1 t2 ON t1.cust_id = t2.cust_id AND
t1.acc_no = t2.acc_no AND
t1.trans_id >= t2.trans_id
GROUP BY t1.cust_id,t1.acc_no,t1.trans_id,t1.trans_type,t1.amount
See SQLFIDDLE : http://www.sqlfiddle.com/#!2/3b5d8/15/0
EDIT :
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE Table1
(`cust_id` int, `acc_no` int, `trans_id` int,
`trans_type` varchar(6), `amount` int)
;
INSERT INTO Table1
(`cust_id`, `acc_no`, `trans_id`, `trans_type`, `amount`)
VALUES
(1111, 1001, 10, 'credit', 2000.0),
(1111, 1001, 11, 'credit', 1000.0),
(1111, 1001, 12, 'debit', 1000.0),
(2222, 1002, 13, 'credit', 2000.0),
(2222, 1002, 14, 'debit', 1000.0)
;
Query 1:
SELECT t1.cust_id,t1.acc_no,t1.trans_id,t1.trans_type,t1.amount,
sum(t2.amount*case when t2.trans_type = 'credit' then 1
else -1 end) as balance
FROM Table1 t1
INNER JOIN Table1 t2 ON t1.cust_id = t2.cust_id AND
t1.acc_no = t2.acc_no AND
t1.trans_id >= t2.trans_id
GROUP BY t1.cust_id,t1.acc_no,t1.trans_id,t1.trans_type,t1.amount
Results:
| CUST_ID | ACC_NO | TRANS_ID | TRANS_TYPE | AMOUNT | BALANCE |
|---------|--------|----------|------------|--------|---------|
| 1111 | 1001 | 10 | credit | 2000 | 2000 |
| 1111 | 1001 | 11 | credit | 1000 | 3000 |
| 1111 | 1001 | 12 | debit | 1000 | 2000 |
| 2222 | 1002 | 13 | credit | 2000 | 2000 |
| 2222 | 1002 | 14 | debit | 1000 | 1000 |
A simple solution is to quantify each transaction (- or +) based on trans_type and then get cumulative sum using window function .
SELECT cust_id,
acc_no,
trans_id,
trans_type,
amount,
Sum (real_amount)
OVER (ORDER BY cust_id) AS balance
FROM (SELECT cust_id,
acc_no,
trans_id,
trans_type,
amount,
( CASE trans_type
WHEN 'credit' THEN amount
WHEN 'debit' THEN amount *- 1
END ) AS real_amount
FROM test) t
You could do this easily through a View, calculating this directly on the table is possible but leads to performance and scalability issues (the database will slow down as the table grows). By using a View the calculation is performed as-needed; if you index the view you can keep the balances up to date without impacting the performance of the transaction table.
If you really insist on it being in the transaction table itself you could possibly use a calculated column which runs a user-defined function to determine the current balance. However this will depend largey on the specific SQL backend you're using.
Here's a basic SELECT Statement which calculates the current balance by Account:
select
acc_no,
sum(case trans_type
when 'credit' then amount
when 'debit' then amount * -1
end) as Amount
from Transactions
group by acc_no
You can use window function:
select cust_id,
acc_no, trans_id, trans_type, amount,
sum(pre_balance) over (partition by cust_id order by trans_id) as balance
from
(select cust_id, acc_no, trans_id, trans_type,
amount,
amount as pre_balance from test
where trans_type = 'credit'
union
select cust_id, acc_no, trans_id, trans_type,
amount, -amount as pre_balance from
test where trans_type = 'debit'
order by trans_id) as sub;
with current_balances as (
SELECT
id,
user_id,
SUM(amount) OVER (PARTITION BY user_id ORDER BY created ASC) as current_balance
FROM payments_transaction pt
ORDER BY created DESC
)
SELECT
pt.id,
amount,
pt.user_id,
cb.current_balance as running_balance
FROM
payments_transaction pt
INNER JOIN
current_balances cb
ON pt.id = cb.id
ORDER BY created DESC
LIMIT 10;
This will work very efficiently for big returns, and won't break on filtering or limiting. Please note that if you select only for one user or a subset of them, provide user_id filter in both current_balances cte, and the main select to omit whole table scan.
Table (Transaction)
-
"id" "amount" "is_credit"
1 10000 1
2 2000 0
3 5000 1
Query :
SELECT *
FROM (
SELECT id, amount, SUM(CASE When is_credit=1 Then amount Else -amount End) OVER (ORDER BY id) AS balance
FROM `Transaction`
GROUP BY id, amount
)
ORDER BY id ;
Output :
"id" "amount" "is_credit" "balance"
1 10000 1 10000
2 2000 0 8000
3 5000 1 13000