Subtraction in same table in same row with where condition #SQL - sql

Using a SQL query to subtract in same table:
Table1
grade record total_amount
------------------------------
DSP receipt 17258
DSP sales 16313
OPC43 receipt 95442
OPC43 sales 92856
OPC53 receipt 371752
OPC53 sales 368985
PPC receipt 156023
PPC sales 152803
vajram receipt 36100
vajram sales 36100
Answer needed
945 DSP
2586 OPC43
2767 OPC53
3220 PPC
0 vajram

Is there ever only two entries per grade? Always just a receipt and sales? If that's the case, then something like the following would do the trick
SELECT receipt.total_amount - sales.total_amount, grade
FROM
(SELECT * FROM yourtable WHERE record = 'receipt') receipt
INNER JOIN (SELECT * FROM yourtable WHERE record = 'sales') sales
ON receipt.grade = sales.grade

I'd try something like that:
select grade, sum(case when record = 'receipt' then total_amount else 0-total_amount end)
from table
group by grade

Related

Conditional left join on max date and where clause in second table

I am attempting to join a customer table with sales table where I show the list of all customers in database and any paid sale the customer might have in the sales tables. Now a customer can have multiple sales rows in the sales table.
This is an example sales record of one customer with multiple sales in the sale tables
while extracting this record I would like to get only the MAX (q_saledatetime) WHERE the q_paidamount is > 0.
as in show me the last time this customer made a payment to us. So in this case row 2 where they paid 8.90 is what I would like to get for that customer. If a customer has no record in the sales table, show their name/details on the list either way.
My failure at the moment is how to include the where clause of the paid amount + max date column.
ATTEMPT A
select DISTINCT ON (q_customer.q_code)
q_customer.q_code, q_customer.q_name, -- customer info
MAX(q_saleheader.q_saledatetime) AS latestDate, q_saleheader.q_paidamount -- saleheader info
FROM q_customer
LEFT JOIN q_saleheader ON (q_customer.q_code = q_saleheader.q_customercode)
group by q_customer.q_code, q_customer.q_name , q_saleheader.q_saledatetime, q_saleheader.q_paidamount
order by q_customer.q_code ASC
which results in
so for Fred Blogg is picking up details from row 4 instead of 2 (first image). As there's no rule for q_paidamount at this point
ATTEMPT B
SELECT
customer.q_code, customer.q_name, -- customer info
sale.q_saledatetime, sale.q_paidamount -- sale info
FROM q_customer customer
LEFT JOIN (SELECT * FROM q_saleheader WHERE q_saledatetime =
(SELECT MAX(q_saledatetime) FROM q_saleheader b1 where q_paidamount > 0 ))
sale ON sale.q_customercode = customer.q_code
which results in
This doesnt seem to be getting any information from the sale table at all.
Update:
After having a closer look at my first attempt I amended the statement and came up with this solution which achieves the same results as Michal's answer. I just curious to know is there any pitfalls or perfomance disadvantages with the following way.
select DISTINCT ON (q_customer.q_code)
q_customer.q_code, q_customer.q_name, -- customer info
q_saleheader.q_saledatetime, q_saleheader.q_paidamount -- saleheader info
FROM q_customer
LEFT JOIN q_saleheader ON (q_customer.q_code = q_saleheader.q_customercode AND
q_saleheader.q_paidamount > 0 )
group by q_customer.q_code, q_customer.q_name , q_saleheader.q_saledatetime,
q_saleheader.q_paidamount
order by q_customer.q_code ASC, q_saleheader.q_saledatetime DESC
main change was adding AND q_saleheader.q_paidamount > 0 on the join and q_saleheader.q_saledatetime DESC to make sure are getting the top row of that related data. As mentioned both Michal's answer and this solution achieve the same results. Just curious about pitfalls in either of the two ways.
Try this query:
SELECT c.q_code,
c.q_name,
CASE WHEN q_saledatetime <> '1900-01-01 00:00:00.000' THEN q_saledatetime END q_saledatetime,
q_paidamount
FROM (
SELECT c.q_code,
c.q_name,
coalesce(s.q_saledatetime, '1900-01-01 00:00:00.000') q_saledatetime, --it will indicate customer with no data
s.q_paidamount,
ROW_NUMBER() OVER (PARTITION BY c.q_code ORDER BY COALESCE(s.q_saledatetime, '1900-01-01') DESC) rn
FROM q_customer c
LEFT JOIN (SELECT q_saledatetime,
q_paidamount
FROM q_saleheader
WHERE q_paidamount > 0) s
ON c.q_code = s.q_customercode
) c WHERE rn = 1

SQL server Currency Code if Invoice has one more than one currency

Below is my requirement
Table1 - QuotationMaster - QuoteID,CustomerID,Date,InvoiceNo
Table2 - QuoteItems - QuoteID,ItemID,CurrencyID,Amount
Table3 - CurrencyMaster - CurrencyID,CurrencyCode
Example:
If I search for the invoice statement for a particular date, final result must be
Date, CustomerName, CurrencyCode, Amount
For a particular invoice if it has 2 items which has same CurrencyID
then result should be displayed as follow
Item1 - Amount (2.00)
Item2 - Amount (3.00)
Date InvoiceNo CustomerName CurrencyCode Amount
June INV123 TESTING INR 5.00
For a particular invoice if it has 2 items which has different CurrencyID's
then result should be displayed as follow
Date InvoiceNo CustomerName CurrencyCode Amount
June INV123 TESTING 0 0.00
To make it simple if a invoice has two items with different currencies then the currency code and Amount must be 0.
I'm not able to get a clear picture how to solve this as I end up with groupby currencyid and get the same result printed twice
Date InvoiceNo CustomerName CurrencyCode Amount
June INV123 TESTING INR 2.00
June INV123 TESTING GBP 3.00
Can you please help me with the approach.
When I understand this right, you basically want to group the items by quoteid and filter for those where the maximum currencyid is no equal to the minimum currencyid in the HAVING clause. (If all currency IDs are equal the minimum is equal to the maximum.)
Then left join this to your quotes table and left join the currency table to get the code.
Check in a case if the currency ID IS NULL. If it is, the items of the quote had different currency ids (or all were NULL which means they were different, by the tri state logic).
As you didn't describe you customer table the customer name isn't selected. You'll have to add that yourself.
SELECT qm1.date,
qm1.invoiceno,
CASE
WHEN qi2.quoteid IS NULL
THEN cm1.currencycode
ELSE
'0'
END currencycode,
CASE
WHEN qi2.quoteid IS NULL
THEN qi2.amount
ELSE
0
END amount
FROM quotationmaster qm1
LEFT JOIN (SELECT qi1.quoteid,
sum(qi1.amount) amount,
max(qi1.currencyid) currencyid
FROM quoteitems qi1
GROUP BY qi1.quoteid
HAVING max(qi1.currencyid) = min(qi1.currencyid)) qi2
ON qi2.quoteid = qm1.quoteid
LEFT JOIN currencymaster cm1
ON cm1.currencyid = cm1.qurrencyid;
You can try this:
SELECT sub.QuoteID,
sub.CustomerID,
sub.Date,
sub.InvoiceNo,
sub.ItemID,
IIF(sub.ItemCount = sub.CurrencyCount, sub.CurrencyID, 0) AS CurrencyID
sub.Amount,
FROM (
SELECT qm.QuoteID,
qm.CustomerID,
qm.Date,
qm.InvoiceNo,
qi.ItemID,
qi.CurrencyID,
qi.Amount,
COUNT(*) OVER (PARTITION BY qi.QuoteID) AS ItemCount,
COUNT(*) OVER (PARTITION BY qi.QuoteID, qi.CurrencyID) AS CurrencyCount
FROM QuotationMaster AS qm
INNER JOIN QuoteItems AS qi
ON qi.QuoteID = qm.QuoteID
) AS sub
-- You can use it also for a join (a 'n/a'-Currency in CurrencyMaster with ID=0 is required)
-- INNER JOIN CurrencyMaster AS cm
-- ON cm.CurrencyID = IIF(sub.ItemCount = sub.CurrencyCount, sub.CurrencyID, 0)
-- OR Use LEFT OUTER JOIN if you dont have a Fallback-Value in CurrencyMaster.

How to find net rows from sales and cancel rows using sql

I have a table with columns mentioned below:
transaction_type transaction_number amount
Sale 2016040433 50
Cancel R2016040433 -50
Sale 2016040434 50
Sale 2016040435 50
Cancel R2016040435 -50
Sale 2016040436 50
I want to find net number of rows with only sales which does not include canceled rows.
(Using SQL Only).
If you just want to count the sales and subtract the cancels (as suggested by your sample data), you can use conditional aggregation:
select sum(case when transaction_type = 'Sale' then 1
when transaction_type = 'Cancel' then -1
else 0
end)
from t;
SELECT count(transaction_type) FROM TBL_NAME GROUP BY count(transaction_type) HAVING transaction_type = 'Sale'
Just use the count function, and use HAVING with GROUP BY to filter
The list of row
select transaction_number, amount
from table
where transaction_type ='Sale';
or sum
select sum(amount)
from table
where transaction_type ='Sale';
or count
select count(*)
from table
where transaction_type ='Sale';
Since you have the transaction number, which i assume is a unique identifier for each transaction, i would use it to count the net sale rows.
Assuming then than every Cancelled transaction number is 'R'+ Sales transact number, then the query i propose is the following:
WITH CancelledSales_CTE (transaction_type, transaction_number, amount)
AS
(
SELECT transaction_type, RIGHT(transaction_number, LEN(transaction_number) - 1), amount
FROM Sales
WHERE transaction_type IS 'Cancel'
)
SELECT count(transaction_number)
FROM Sales
where transaction_number not in
(select distinct transaction_number from CancelledSales_CTE )
CancelledSales_CTE contains the list of cancelled rows, with the transaction number of the relative sale.
The count coming from
SELECT count(transaction_number)
FROM Sales
where transaction_number not in
(select distinct transaction_number from CancelledSales_CTE )
will exclude the transaction numbers which have been canceled

SQL Query to evaluate multiple rows

I would like to retrieve only the customers from an order table who have paid for all the orders. (Paid = 'Y').
The order table looks like this:
Cus # Order # Paid
111 1 Y
111 2 Y
222 3 Y
222 4 N
333 5 N
In this example the query should only return customer 111.
The query
Select * from order where Paid = 'Y';
returns customers that have paid and unpaid orders (ex. customer 222) in addition to customers who have paid for all of their orders (customer 111).
How do I structure the query to evaluate all the orders for a customer and only return information for a customer that has paid for all the orders?
Looking the problem a different way, you need only the customers who don't have any unpaid order.
sel cus from order group by Cus having min(Paid) = 'Y';
The above query also utilizes the fact that 'Y' > 'N'.
SQL Fiddle:
http://sqlfiddle.com/#!4/f6022/1
If you need to select all different orders for eligible customers, you may use OLAP functions:
select cus,order,paid from (select cus,order,paid,min(paid)
over (partition by cus)minz from order)dt where minz='Y';
With Oracle, you can also do select customer from your_table where paid='Y' minus select customer from your_table where paid='N', although I don't know if this is faster and, of course, you don't get the other fields in this case.
Don't know Oracle but often work in SQL Server, I would write query something like this
Select Cus from order group by Cus having count(*) = sum(case when Paid = ‘Y’ then 1 else 0);
Basically you retrieve customers where total order count equals to sum or paid orders.
Again, apologize for not giving proper Oracle syntax but hopefully that will point you to the right direction.
SELECT *
FROM orders oo
WHERE NOT EXISTS
(
SELECT 1
FROM orders io
WHERE oo.cus# = io.cus#
AND io.paid = 'N'
)
;

SQL Server: How to calculate different sums in a single query

A friend has suggested I post here as I'm in need of a bit of help!
DB Layout:
**salestable**
salesorder [primary, unique] (sales order number)
salesman (salesperson id)
**salesline**
salesorder [many sales line to single salestable relationship]
saleprice (line amount)
isaccessory (0 or 1)
I'd like to, in a single select, sum sales price as a total per salesman, but also sum sales price where is accessory = 1.
The grouping and joins aren't what I'm stuck on, it's whether I can do the dual sum.
In my head it would look something like
...salesman,sum(amount) as totalsales,sum(amount where accessory=1) as accessorysales...
But obviously that would never work!
Thanks in advance! (PS, I'm very new here, be gentle...)
SELECT salesman, SUM(amount), SUM(CASE WHEN accessory = 1 THEN amount ELSE 0 END)
FROM salestable t
JOIN salesorder o
ON o.salesorder = t.salesorder
GROUP BY
salesman