SQL SERVER MULTIPLE JOIN: avoid duplicate values - sql-server

I have 3 tables in my db:-
Customer:
CId | CName | CLocation | CJoinDate
Payment:
TxnId | TxStatus | TxComment | TxAmount | CId | TxDate
Appointment:
AppId | AppCode | CId | ADate | AComment
When I do a Left join with two tables then the calculated results come right. But when I try to do join with 3 tables then the result calculated is wrong.
for eg:-
If I try this query then the total amount calculated is correct:
SELECT c.CName, sum(p.TxAmount)
FROM Customer c LEFT JOIN Payment p ON c.CId = p.CId
WHERE p.TxStatus = 1
GROUP BY c.CName;
In the above query I am just joining two tables which gives me the correct result.
Now when I want to show all the records in one table so I had to join 3 tables.
Below is the query I tried:
SELECT c.CName as Name, sum(p.TxAmount) as Payment, count(distinct a.ADate) as Total_Visit
FROM Customer c LEFT JOIN Payment p ON c.CId = p.CId LEFT JOIN Appointment a ON c.CId = a.CId
WHERE p.TxStatus = 1
GROUP BY c.CName;
The above query gives me wrong Payment amount for each customers. The reason for the wrong result is the Appointments table has more rows as compared to Payment table for each customer. So to show all the appointment entries, the payment amount gets duplicated coz of which the calculation gets wrong.
How can I fix the above scenario witht the above query.
Tks
EDIT: Actually theres 2-3 more tables which I have to join similiar to Appointment table along with a GROUP BY clause for each month.
EDIT1: Fixed it by multiple CTE. Thanks for your valuable pointers it was indeed helpful.

Use a simple CTE expression if you are sure that your sum is calculated correctly by the first query
WITH cte AS
(
SELECT c.CName, c.CID, sum(p.TxAmount) AS sumAmount
FROM Customer c LEFT JOIN Payment p ON c.CId = p.CId
WHERE p.TxStatus = 1
GROUP BY c.CName, c.CID
)
SELECT cte.CName, cte.sumAmount, count(distinct a.ADate) as Total_Visit
FROM cte LEFT JOIN Appointment a ON c.CId = a.CId
GROUP BY c.CName, cte.sumAmount

Try to use a sub query:
SELECT
c.CName as Name
, sum(p.TxAmount) as Payment
, Total_Visit = (SELECT count(distinct a.ADate) FROM Appointment a ON c.CId = a.CId)
FROM Customer c
LEFT JOIN Payment p ON c.CId = p.CId
WHERE p.TxStatus = 1
GROUP BY c.CName;

You can calculate total_visit per CId and then join the subquery
SELECT
c.CName as Name
, sum(p.TxAmount) as Payment
, Total_Visit
FROM Customer c
LEFT JOIN Payment p ON c.CId = p.CId
LEFT JOIN (SELECT a.CId, count(distinct a.ADate) Total_visit FROM Appointment a group by a.CId) as a on c.CId = a.CId
WHERE p.TxStatus = 1
GROUP BY c.CName;

Related

my query doesn't work as expected

SELECT Product.ProductName
,SUM(Purchase.Value) AS TotalPurchase
,SUM(Sales.Value) AS TotalSales
,((TotalPurchase) - (TotalSales)) AS ProductAvailability
FROM Product
INNER JOIN Purchase ON Product.ProductID = Purchase.ProductID
INNER JOIN Sales ON Product.ProductID = Sales.ProductID
GROUP BY Product.ProductName
I have 3 Table Product - Sales - Purchase
and i want Show product name total sales each product ,total purchase of each
product And how many of those product still unsold
SELECT Product.ProductName
,SUM(Purchase.Value) AS TotalPurchase
,ISNULL(SUM(Sales.Value),0) AS TotalSales
,(SUM(Purchase.Value) - ISNULL(SUM(Sales.Value),0)) AS ProductAvailability
FROM Product
LEFT JOIN Purchase ON Product.ProductID = Purchase.ProductID
LEFT JOIN Sales ON Product.ProductID = Sales.ProductID
GROUP BY Product.ProductName
Since you also want to show the unsold products , there will be no data for them products in the sales table, hence you need a LEFT JOIN here not an INNER JOIN.
Also the columns TotalPurchase and TotalSales are available to be called in the query they are being calculated , hence use a sub-query to manipulate these columns or use the expression itself.
After 5 Hours OF playing With Query I FOUND THE ANSWER
SELECT PN.ProductName,TotalPurchase ,TotalSales,TotalPurchase-TotalSales AS ProductAvailability
FROM
((SELECT SUM(Purchase.Value) AS TotalPurchase,Purchase.ProductID FROM Purchase GROUP BY Purchase.ProductID ) AS TP
INNER JOIN
(SELECT SUM(Sales.Value) AS TotalSales,Sales.ProductID FROM Sales GROUP BY Sales.ProductID ) AS TS
ON tp.ProductID=TS.ProductID
INNER JOIN
(SELECT Product.ProductName,Product.ProductID FROM Product GROUP BY Product.ProductName,ProductID) AS PN ON PN.ProductID=TS.ProductID
)

create sql query to fetch repeat column values within time frame

Can someone help me with this query? I want to get the result of all the customer_id which repeats more than once in 24hrs
SELECT
O.Order_No, O.Customer_ID, O.DateOrdered, O.IPAddress,
C.FirstName, C.LastName, CD.nameoncard
FROM
Order_No O
INNER JOIN
CardData CD ON O.card_id = CD.id
INNER JOIN
Customers C ON O.customer_id = C.customer_id
ORDER BY
O.order_no desc
adding more details..
so suppose order with customer id xx was placed on 04/23 2:30 pm and again 2nd order was placed with same customer Id xx on same day 04/23 5:30 pm.
i want the query to return me customer Id xx
Thanks
select Customer_ID, CAST(DateOrdered as Date) DateOrdered, count(*) QTDE
from Order_No
group by Customer_ID, CAST(DateOrdered as Date)
having count(*) > 1
To get the customers who have orders issued after the first one, then you could use the following query:
select distinct A.Customer_ID
from Order_No A
inner join (select Customer_ID, min(DateOrdered) DateOrdered from Order_No group by Customer_ID ) B
on A.Customer_ID = B.Customer_ID
and A.DateOrdered - B.DateOrdered <= 1
and A.DateOrdered > B.DateOrdered
SQL Fiddle
To get all customers that have ANY TIME more than one order issued in period less or equal than 24h
select distinct A.Customer_ID
from Order_No A
inner join Order_No B
on A.Customer_ID = B.Customer_ID
and A.DateOrdered > B.DateOrdered
and A.DateOrdered - B.DateOrdered <= 1
SQL Fiddle
Self-join:
SELECT distinct O.Customer_ID
FROM
Order_No O
inner join Order_No o2
on o.customerID = o2.customerID
and datediff(hour, o.DateOrdered, o2.DateOrdered) between 0 and 24
and o.Order_No <> o2.Order_No
This will return all customer_IDs that have ever placed more than one order in any 24 hour period.
Edited to add the join criteria that the matching records should not be the same record. Should return customers who placed two different orders at the same time, but not customers who placed only one order.

Error when using WHERE in Correlated Subquery

I have the following relationship: Bank -> Financing -> Contracts -> Supplier
I have to select all the Banks that is related to a Supplier, using the query below:
SELECT DISTINCT A.Name AS BankName, A2.Name as SupplierName
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name
So far, so good.
Now I have to list the number of contracts that are active and the number of total contracts, so I thought about including a subquery:
SELECT DISTINCT A.Name AS BankName, A2.Name as SupplierName,
(SELECT COUNT(C.Id)),
(SELECT COUNT(C.Id) WHERE C.ContractStatus = 1)
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name
Line 2 Works well, but I got na error in line 3:
Column 'Contracts.ContractStatus' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I expect the following result:
BANK NAME | SUPPLIER NAME | TOTAL CONTRACTS | ACTIVE CONTRACTS
Bank 1 | Supplier 1| 5 | 2
How can I achieve this? I'm using SQL Server 2014
Thanks in advance!
Remove the distinct since your group by and use CASE
SELECT A.Name AS BankName, A2.Name as SupplierName,
COUNT(C.Id),
SUM(CASE WHEN C.ContractStatus = 1 THEN 1 ELSE 0 END)
FROM Companies A
INNER JOIN Financing F ON F.IdFinancialCompany = A.Id
INNER JOIN Contracts C ON F.IdContract = C.Id
INNER JOIN Companies A2 ON C.IdSupplier = A2.Id
GROUP BY A.Name, A2.Name

SQL Join on 3 tables

Here is my query:
select custnmbr,custname,slprsnid,cdatetime,cdur,cnumber,cext,
finalcalledpartynumber,sono,invno,ordamt,invamt,adduser
from table1 calls left join table2 cust
on (calls.number = cust.phone1 or calls.cext = cust.phone1)
left outer join table3 sales on (cust.custnmbr = sales.custno
and sales.adddate = #date)
where (cnumber = #phone or cext = #phone) and cdatetime >= #date
Here is what I am trying to do:
Get all the calls from table 1, and get the customer from table 2. Then get all the sales from table 3 and the customer from table 2.
What I am getting is all the calls, the customer, and then if there is an order for that customer I get that as well. What I want is all the orders as well.
Just looking for some pointers on joining 3 tables.
You have to think about how you're going to handle the calls and sales results since there does not appear to be a correlation between calls and sales. So, a customer with 3 calls and 4 sales will produce 12 rows in the result set. This is where we could help you better if you provide more specifics.
The concept is that if calls is a necessary requirement, then you could do something like...
SELECT ca.CallId, ca.TimeStarted, ...,
c.CustomerId, c.FirstName, ...,
s.SaleDate, s.SaleAmount, ...
FROM calls AS ca
INNER JOIN customers AS c ON ca.CustomerId = c.CustomerId
OUTER APPLY ( --or CROSS APPLY, depending on your needs
SELECT s.SaleDate, s.SaleAmount, ...
FROM sales AS s
WHERE s.CustomerId = c.CustomerId
AND s.SaleDate = ca.CallDate --It would help if this relationship existed
)
Generally I would start with the table you want ALL the data from, the one that you are going to form the basis of your navigation to the other entities.:
SELECT
C.*,
Ca.*,
S.*
FROM Sales S
LEFT JOIN Customer C
ON (S.CustomerId = C.CustomerId) -- your condition
LEFT JOIN Calls Ca
ON (C.CustomerId = Ca.CustomerId) -- your condition

SQL Complex Select - Trouble forming query

I have three tables, Customers, Sales and Products.
Sales links a CustomerID with a ProductID and has a SalesPrice.
select Products.Category, AVG(SalePrice) from Sales
inner join Products on Products.ProductID = Sales.ProductID
group by Products.Category
This lets me see the average price for all sales by category. However, I only want to include customers that have more than 3 sales records or more in the DB.
I am not sure the best way, or any way, to go about this. Ideas?
You haven't mentioned the customer data anywhere so I'll assume it's in the Sales table
You need to filter and restrict the Sales table first to the customers with more the 3 sales, then join to get product category and get the average across categories
select
Products.Category, AVG(SalePrice)
from
(SELECT ProductID, SalePrice FROM Sales GROUP BY CustomerID HAVING COUNT(*) > 3) S
inner join
Products on Products.ProductID = S.ProductID
group by
Products.Category
I'd try the following:
select Products.Category, AVG(SalePrice) from Sales s
inner join Products on Products.ProductID = s.ProductID
where
(Select Count(*) From Sales Where CustomerID = s.CustomerID) > 3
group by Products.Category
I'd create a pseudo-table of "big customer IDs" with a select, and then join it to your query to limit the results:
SELECT Products.Category, AVG(SalePrice) FROM Sales
INNER JOIN Products ON Products.ProductID = Sales.ProductID
INNER JOIN (
SELECT CustomerID FROM Sales WHERE COUNT(CustomerID) >= 3 GROUP BY CustomerID
) BigCustomer ON Sales.CustomerID = BigCustomer.CustomerID
GROUP BY Products.Category
Too lazy to test this out though, so let me know if it works ;o)
Another way
;WITH FilteredSales AS
(
SELECT Products.Category, Sales.SalesPrice, COUNT(Sales.CustomerId) OVER(PARTITION BY Sales.CustomerId) AS SaleCount
FROM Sales
INNER JOIN Products ON Products.ProductID = Sales.ProductID
)
select Category, AVG(SalePrice)
from FilteredSales
WHERE SaleCount > 3
group by Category

Resources