SQL query for insert into with update on duplicate key - sql

I have two tables debitTable and creditTable.
debitTable has the following records:
+----+-------+
| id | debit |
+----+-------+
| a | 10000 |
| b | 35000 |
+----+-------+
and creditTable has these records:
+----+--------+
| id | credit |
+----+--------+
| b | 5000 |
+----+--------+
How about the SQL Server query to produce these results:
+----+----------------+--------------+
| id | debit | credit | debit-credit |
+----+----------------+--------------+
| a | 10000 | 0 | 10000 |
| b | 35000 | 5000 | 30000 |
+----+-------+--------+--------------+

You want to use a join. However, it is important to aggregate before joining:
select coalesce(d.id, c.id) as id, coalesce(credit, 0) as credit,
(coalesce(debit, 0) - coalesce(credit, 0)) as DebitMinusCredit
from (select id, sum(debit) as debit
from debit
group by id
) d full outer join
(select id, sum(credit) as credit
from debit
group by id
) c
on d.id = c.id;
This uses full outer join to ensure that all records from both tables are included, even if an id is not in one of the tables. The aggregation before joining is to avoid Cartesian products when there are multiple rows for a single id in both tables.

You can try "Left Join"
Select *
from debit d
left join credit c on d.id = c.id

select
debit.id, debit.debit, credit.credit,
debit.debit - credit.credit as [debit-credit]
from
debit
left join
credit on debit.id = credit.id
BUT this will be based only on debit: meaning if you have id in credit which is not in debit it won't appear in this result.

Related

How can I join two columns that both reference the same column in another table (Postgres SQL)?

Example
Accounts Table:
id | name
1 | Checking
2 | Visa
Transactions Table:
date | description | amount | from_id | to_id
10-8 | payment | $100 | 1 | 2
Question:
How can I query the Transactions table and get the name for the from_id and to_id columns which both reference the id column in the Accounts table?
Using the example above, I'm trying to return:
date | description | amount | from | to
10-8 | payment | $100 | Checking | Visa
you have to join it twice:
select t.date, t.description, t.amount, a_from.name fromname, a_to.name toName
from transactions t
join accounts a_to on t.to_id = a_to.id
join accounts a_from on t.from_id = a_from.id

Return rows from a table and add field for that row if the ID has a relationship with another table

DBMS used: Amazon Aurora
I have a table that I store a list of all my products, let's call it products
+----+--------------+
| id | product_name |
+----+--------------+
| 1 | Product 1 |
+----+--------------+
| 2 | Product 2 |
+----+--------------+
| | |
+----+--------------+
Another table called redeemed_products stores the ID of the product that the user has redeemed.
+----+---------+------------+
| id | user_id | product_id |
+----+---------+------------+
| 1 | 1 | 1 |
+----+---------+------------+
| | | |
+----+---------+------------+
| | | |
+----+---------+------------+
I would like to retrieve all rows of products and add an extra field to the row which has a relation in the redeemed_products
+----+--------------+----------+
| id | product_name | redeemed |
+----+--------------+----------+
| 1 | Product 1 | true |
+----+--------------+----------+
| 2 | Product 2 | |
+----+--------------+----------+
| | | |
+----+--------------+----------+
The purpose of this is to retrieve the list of products and it will show which of the product has already been redeemed by the user. I do not know how I should approach this problem.
Use an outer join:
select p.id, p.product_name, rp.product_id is not null as redeemed
from products p
left join redeemed_products rp on rp.product_id = p.id;
Note that this will repeat rows from the products table if the product_id occurs more than once in the redeemed_products table (e.g. the same product_id for multiple user_ids).
If that is the case you could use a scalar sub-select:
select p.id, p.product_name,
exists (select *
redeemed_products rp
where rp.product_id = p.id) as redeemed
from products p;
You haven't tagged your DBMS, but the above is standard ANSI SQL, but not all DBMS products actually support boolean expressions like that in the SELECT list.
One option would be using a conditional within a LEFT JOIN query :
SELECT p.*, CASE WHEN r.product_id IS NOT NULL THEN 'true' END AS redeemed
FROM products p
LEFT JOIN redeemed_products r
ON r.product_id = p.id

How to query for SUM of multiple columns one-to-many

I have the following tables:
| Sales.Transaction
| ---------------
| Id
| Date
| BranchId
| Commission
|----------------
| Sales.TransactionItem
| ------------------
| Id
| Rate
| Pages
| TransactionId
|-------------------
| Sales.Branch
|-------------
| Id
| Name
|-------------
How can I get the total sales of each Branches, total number of transactions and total pages? I need to have a shape of data like this:
NOTE: Total amount of Transaction, can be computed by getting the sum of TransactionItems(Rate * Pages) - Commission
| Branches | Total Sales | No. of Transactions | Total Pages |
| Branch A | 10,500 | 14 | 17 |
| Branch B | 5,200 | 4 | 4 |
| Branch C | 400 | 2 | 2 |
| Branch D | 6,100 | 8 | 14 |
The problem with my query is that when the Transaction has a Commission and more than one TransactionItems, the Commission is being multiplied by the number of TransactionItems
select
b.Name as BranchName,
COUNT(t.Id) as Transactions,
SUM(ti.Pages * ti.Rate) - SUM(t.Commission) as TotalSales,
SUM(ISNULL(ti.Pages, 0)) as Pages
from
Sales.Branch b
left join Sales.[Transaction] t
on b.Id = t.BranchId
and t.Date >= '2017-11-01'
AND t.Date < '2017-12-01'
left join Sales.TransactionItem ti
on ti.TransactionId = t.Id
group by b.Name
order by b.Name ASC
This is tricky -- I think the solution is to aggregate the transaction items before joining the rest of the tables together:
select b.Name as BranchName,
count(t.Id) as Transactions,
sum(ti.total_minus_commission) - SUM(t.Commission) as TotalSales,
sum(ti.total_pages) as Pages
from Sales.Branch b left join
Sales.[Transaction] t
on b.Id = t.BranchId and
t.Date >= '2017-11-01'
t.Date < '2017-12-01' left join
(select ti.TransactionId,
sum(ti.Pages * ti.Rate) as total_minus_commission,
sum(ti.Pages) as total_pages
from Sales.TransactionItem ti
group by ti.TransactionId
) ti
on ti.TransactionId = t.Id
group by b.Name
order by b.Name ASC;
Note: I also think this correctly calculates Transactions.

SQL query to get table rows whose columns should not match with other table columns

Thanks in advance,
Actually I have two tables carts and checks. Carts table contains rows as below
id |username |orderid | exam_name | price
1 | Rajesh | ABC123 | PMP | $60
2 | Rajesh | ABC123 | CPM | $70
3 | David | ABC789 | ITIL | $80
checks table contains rows as below
id |username |order_id | exam | price
1 Rajesh | ABC123 | PMP | $60
2 Rajesh | ABC123 | CPM | $70
I need a row data of carts table whose orderid column and exam_name column should not match with checks table order_id column and exam column
Something like this as below:
id |username |orderid | exam_name | price
1 | David | ABC789 | ITIL | $80
One method is not exists:
select c.*
from carts c
where not exists (select 1
from checks ch
where ch.orderid = c.orderid and ch.exam_name = c.exam_name
);
A similar method is left join:
select c.*
from carts c left join
checks ch
on ch.orderid = c.orderid and ch.exam_name = c.exam_name
where ch.orderid is null;
And in some databases you can use not in:
select c.*
from carts c
where (c.orderid, c.exam_name) not in (select ch.orderid, ch.exam_name from checks ch);
select c.*
from carts c
left join checks ch on c.id = ch.id
where ch.id is null;
Hope this should solve your problem.
SELECT *
from Carts
where exam_name not in (select exam from checks)
and id not in (select id from checks)
SELECT * FROM Carts
WHERE NOT EXISTS (SELECT 1 FROM checks
WHERE checks.OrderId = Carts.OrderId
and Carts.exam_name = checks.exam_name)

Issue with SQL involving JOINS

I have 2 tables with similar layout, involving INCOME and EXPENSES.
The id column is a customer ID.
I need a result of customer TOTAL AMOUNT, summing up income and expenses.
Table: Income
| id | amountIN|
+--------------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
Table: Expenses
| id | amountOUT|
+---------------+
| 1 | -x |
| 4 | -z |
My problem is that some customers only have expenses and others just income... so cannot know in advance id I need to do a LEFT or RIGHT JOIN.
In the example above an RIGHT JOIN could do the trick, but if the situation is inverted (more customers on the Expenses table) it doesn't work.
Expected Result
| id | TotalAmount|
+--------------+
| 1 | a - x |
| 2 | b |
| 3 | c |
| 4 | d - z |
Any help?
select id, SUM(Amount)
from
(
select id, amountin as Amount
from Income
union all
select id, amountout as Amount
from Expense
) a
group by id
I believe a full join will solve your problem.
I would approach this as a union. Do that in your subquery then sum on it.
For instance:
select id, sum(amt) from
(
select i.id, i.amountIN as amt from Income i
union all
select e.id, e.amountOUT as amt from Expenses e
)
group by id
You should really have another table like client :
Table: Client
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
So you could do something like that
SELECT Client.ID, COALESCE(Income.AmountIN, 0) - COALESCE(Expenses.AmountOUT, 0)
FROM Client c
LEFT JOIN Income i ON i.ID = c.ID
LEFT JOIN Expense e ON e.ID = c.ID
Will be less complicated and i'm sure it will come handy another time :)