SQL Query SUM with conditions - sql

I'm having some difficulties finding the good query I need, looking for help.
After multiple JOIN and a lot of WHERE, I have this columns and values :
+---------+----------+--------+-------+
| id_test | quantity | price | fee |
+---------+----------+--------+-------+
| "1-3" | 1 | 33.52 | 29.00 |
| "1-5" | 1 | 33.52 | 29.00 |
| "1-6" | 1 | 33.52 | 29.00 |
| "1-8" | 1 | 86.49 | 29.00 |
| "19-1" | 1 | 176.54 | 29.00 |
| "19-4" | 1 | 176.54 | 29.00 |
| "19-5" | 4 | 176.54 | 29.00 |
| "19-6" | 1 | 199.47 | 29.00 |
| "19-6" | 1 | 176.54 | 29.00 |
| "20-10" | 2 | 72.67 | 29.00 |
| "20-11" | 2 | 18.95 | 29.00 |
| "20-9" | 1 | 22.13 | 29.00 |
+---------+----------+--------+-------+
Each id_test object as a quantity, a price and a fee. I would like to SUM everything so to have a global price : (quantity*price)+fee
The thing is, and that's where I'm stuck, the fee has to be added only once by id_test. Yet, I have here two id_test "19-6".
If I SUM everything I have 1827.67 (price*quantity) + 348 (fee).
Since I have two "19-6", I need 1827.67 (price*quantity) + 319 (fee).
How would you do in SQL ?
Thanks

You can make use of Common Table Expressions (CTE)
Assuming your data in "data" table:
with total_fee as (
select distinct id_test, fee from data
),
total_price as (
select distinct id_test, sum(quantity) quantity, sum(price) price
from data
group by id_test
)
select sum(b.price*quantity) quantity, sum(a.fee) fee
from total_fee a
left join total_price b
on a.id_test = b.id_test

Only once by id_test means you have to take distinct id_test. If 29.00 is a fixed static value, you can take the distinct id_test and multiply it by 29.00
You can Insert the values of the above query to a temporary table ( #tempTable). Then your above output values will be temporary saved to a table
SELECT
DISTINCT(id_test) * 29.00 AS fee
FROM
#tempTable

select sum(quantity * price),
(select sum(fee)
from (select distinct(id_test), fee
from sales) as fees)
from sales;
I created a SQLFiddle to demonstrate.

You can use row_number()
SELECT SUM(price*quantity) + SUM(CASE WHEN rn = 1 THEN fee END) AS RESULT
FROM (SELECT *,ROW_NUMBER() OVER ( partition BY id_test) rn
FROM test) m
CHECK DEMO HERE
OUTPUT
Result
2146.67

Put id_test, (quantity * price) & fee in a temporary table.
WITH cte1 AS (
SELECT
id_test,
(quantity * price) AS total,
fee
FROM
table
)
Now add the total for every id
WITH cte2 AS (
SELECT SUM(total) + fee AS total_id
FROM cte1
GROUP BY id_test
)
Now find the total sum
SELECT SUM(total_id) AS SUM
FROM cte1

Just use aggregation:
select sum(price * quantity) + fee
from t
group by id_test, fee;
If you have different fees for the same test, you might want:
select sum(price * quantity) + avg(fee)
from t
group by id_test;

Related

Get total count of duplicates in column

I need a query to count the total number of duplicates in a table, is there any way to do this?
If I have a table like this:
+------------+----------+
| item_name |quantity |
+------------+----------+
| Calculator | 89 |
| Notebooks | 40 |
| Pencil | 40 |
| Pens | 32 |
| Shirts | 29 |
| Shoes | 29 |
| Trousers | 29 |
+------------+----------+
I can't use SELECT COUNT(quantity) because it returns 2. (40 | 29)
How can I return 5? (40 | 40 | 29 | 29 | 29)
Using analytic functions:
WITH cte AS (
SELECT *, COUNT(*) OVER (PARTITION BY quantity) cnt
FROM yourTable
)
SELECT COUNT(*)
FROM cte
WHERE cnt > 1;
One method uses two levels of aggregation:
select sum(cnt)
from (select quantity, count(*) as cnt
from t
group by quantity
) t
where cnt > 1;
Interestingly, if you wanted "3" -- the number of rows that have duplicates, you could express this as:
select count(*) - count(distinct quantity)
from t;
But that is not what you are asking for.

SQL: SELECT all and Remove duplicated rows based on values from multiple columns

I have the following SQL table name 'orders':
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
| 111 | 2345 | 45 | 3 |
| 111 | 1234 | 23 | 1 |
+--------------+------------+-------------------+------------+
And I'm trying to Select only the rows that the order_id and item_id are NOT the same (remove duplicates only if BOTH has the same value), I tried using "Group_By" as follows:
SELECT * FROM orders GROUP BY order_id,item_id
but this, will remove all duplicates of order_id, and all duplicated of item_id, here is the result:
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
+--------------+------------+-------------------+------------+
I've tried using 'DISTINCT' too, but I need to select all columns in the result.
here is the expected result:
+--------------+------------+-------------------+------------+
| order_id | item_id | amount | commission |
+--------------+------------+-------------------+------------+
| 111 | 1234 | 23 | 1 |
| 222 | 1234 | 34 | 2 |
| 111 | 2345 | 45 | 3 |
+--------------+------------+-------------------+------------+
I hope its clear, Thank you.
You can use row_number():
select order_id, item_id, amount, commission
from (
select t.*, row_number() over(partition by order_id, item_id order by commission) rn
from mytable t
) t
where rn = 1
With your sample data, it is not easy to see exactly which partition and order by clause you are looking for, so you might need to adjust them to your exact use case.
Simply:
SELECT DISTINCT * FROM orders
SELECT DISTINCT should do what you want
SELECT DISTINCT order_id, item_id, amount, commission
FROM orders;
If you have more columns, but only care about these being duplicated, then you can use ROW_NUMBER():
SELECT o.*
FROM (SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY order_id, item_id, amount, commission ORDER BY (SELECT NULL)) as seqnum
FROM orders o
) o
WHERE seqnum = 1;
You can try this.
create table MyTable
(order_id int
, item_id int
, amount int
, commission int)
insert into MyTable values
(111, 1234, 23, 1),
(222, 1234, 34, 2),
(111, 2345, 45, 3),
(111, 1234, 23, 1)
select distinct * from MyTable
Live db<>fiddle demo.

Retrieve the minimal create date with multiple rows

I have an issue with an SQL query that I am trying to write. I am trying to retrieve the row that has the minimal create_dt for each inst (see table) and amount (which isn't unique).
Unfortunately I can't use group by as the amount column isn't unique.
+--------------+--------+------+-------------+
| Company_Name | Amount | inst | Create Date |
+--------------+--------+------+-------------+
| Company A | 1000 | 4545 | 01/10/2018 |
| Company A | 400 | 4545 | 01/11/2018 |
| Company A | 200 | 4545 | 31/10/2018 |
| Company B | 2000 | 4893 | 01/10/2016 |
| Company B | 212 | 4893 | 04/10/2016 |
| Company B | 100 | 4893 | 10/10/2017 |
| Company B | 20 | 4893 | 04/10/2018 |
+--------------+--------+------+-------------+
In the above example I expect to see:
+--------------+--------+------+-------------+
| Company_Name | Amount | inst | Create Date |
+--------------+--------+------+-------------+
| Company A | 1000 | 4545 | 01/10/2018 |
| Company B | 2000 | 4893 | 01/10/2016 |
+--------------+--------+------+-------------+
Code:
SELECT
bill_company, bill_name, account_no
FROM
dbo.customer_information;
SELECT
balance_id, balance_id2, minus_balance,new_balance,
create_date, account_no
FROM
dbo.btr
SELECT
balance_id, balance_id2, expired_Date, amount, balance_type, account_no
FROM
dbo.btr_balance
SELECT
balance_ist, expired_date, account_no, balance_type
FROM
dbo.BALANCE_inst
Retrieve the minimal create data for a balance instance with the lowest balance for a balance inst.
(SELECT
bill_company,
bill_name,
account_no,
balance_ist,
amount,
MIN(create_date)
FROM
dbo.mtr btr
LEFT JOIN
btr_balance btrb ON btr.balance_id = btrb.balance_id
AND btr.balance_id2 = btrb.balance_id2
LEFT JOIN
balance_inst bali ON btr.account_no = bali.account_no
AND btrb.expired_date = bali.expired_date
GROUP BY
bill_company, bill_name, account_no,amount, balance_ist)
I have seen some solutions about using correlated query but can't see to get my head around it.
Common Table Expression (CTE) will help you.
;with cte as (
select *, row_number() over(partition by company_name order by create_date) rn
from dbo.myTable
)
select * from cte
where rn = 1;
use row_number() i assumed bill_company is your company name
select * from
( SELECT bill_company,
bill_name,
account_no,
balance_ist,
amount,
create_date,
row_number() over(partition by bill_company order by create_date) rn
FROM dbo.mtr btr left join btr_balance btrb
on btr.balance_id = btrb.balance_id and btr.balance_id2 = btrb.balance_id2
left join balance_inst bali
on btr.account_no = bali.account_no and btrb.expired_date = bali.expired_date
) t where t.rn=1

Sum across columns and rows

Consider a table like this
table
+--------+---------+-----------+---------+-----------+
| BookId | ItemId1 | Quantity1 | ItemId2 | Quantity2 |
+--------+---------+-----------+---------+-----------+
| 1 | 1 | 2 | 2 | 1 |
| 1 | 3 | 1 | 2 | 1 |
| 2 | 1 | 1 | 2 | 1 |
+--------+---------+-----------+---------+-----------+
Now I want to get the sum of columns quantity for each item grouped by book. How can I take the sum across different columns then? right now I use an awkward solution like building a temporary table and then querying this one, but it must be possible in a more elegant way!?
select
BookId 'BookId',
ItemId1 'ItemId',
Quantity1 'Quantity'
into #temptab
from table
union all
select
BookId,
ItemId2,
Quantity2
from table
and after that
select
BookId,
ItemId,
sum(Quantity)
from #temptab
group by ItemId, BookId
How can I get rid of this intermediate step?
Desired output:
+--------+--------+----------+
| BookId | ItemId | Quantity |
+--------+--------+----------+
| 1 | 1 | 2 |
| 1 | 3 | 1 |
| 1 | 2 | 2 |
| 2 | 1 | 1 |
| 2 | 2 | 1 |
+--------+--------+----------+
Use cross apply with table valued constructor to unpivot the data then find sum per bookid and item.
This will avoid your intermediate step
SELECT BookId,
item,
Sum(quantity)
FROM Youratble
CROSS apply (VALUES(Quantity1,ItemId1),
(Quantity2,ItemId2))cs(quantity, item)
GROUP BY BookId,
item
As mentioned by Mikael Eriksson for sql server 2005 use this
SELECT BookId,
item,
Sum(quantity)
FROM Youratble
cross apply
(select Quantity1, ItemId1
union all
select Quantity2, ItemId2) as cs(quantity, item)
GROUP BY BookId,
item
Based on what you've done, you can do it in one single step using subqueries:
select
combined.BookId,
combined.ItemId,
sum(combined.Quantity)
from
(select
BookId 'BookId',
ItemId1 'ItemId',
Quantity1 'Quantity'
from tableName
union all
select
BookId,
ItemId2,
Quantity2
from tableName) as combined
group by combined.ItemId, combined.BookId

Inconsistent Transpose

Given a table A has the following data:
+----------+-------+
| Supplier | buyer |
+----------+-------+
| A | 1 |
| A | 2 |
| B | 3 |
| B | 4 |
| B | 5 |
+----------+-------+
My question is, can I transpose the second column so the resultant table will be like:
+----------+--------+--------+--------+
| Supplier | buyer1 | buyer2 | buyer3 |
+----------+--------+--------+--------+
| A | 1 | 2 | |
| B | 3 | 4 | 5 |
+----------+--------+--------+--------+
Assuming the maximum number of buyers is known as three.
You could use a common table expression to give each buyer an order within the supplier, and then just do a regular case to put them in columns;
WITH cte AS (
SELECT supplier, buyer,
ROW_NUMBER() OVER (PARTITION BY supplier ORDER BY buyer) rn
FROM Table1
)
SELECT supplier,
MAX(CASE WHEN rn=1 THEN buyer END) buyer1,
MAX(CASE WHEN rn=2 THEN buyer END) buyer2,
MAX(CASE WHEN rn=3 THEN buyer END) buyer3
FROM cte
GROUP BY supplier;
An SQLfiddle to test with.
You may consider using PIVOT clause:
select *
from (
select supplier, buyer, row_number() over (partition by supplier order by buyer) as seq
from a
)
pivot (max(buyer) for seq in (1 as buyer1, 2 as buyer2, 3 as buyer3));
SQLFiddle here.