Join SQL query not giving desired result - sql

I have separate queries with me as follows:
SELECT c.id
FROM claim c,
company co,
customer cu
WHERE c.company_id = co.id
AND c.customer_id = cu.id
AND co.company_code = 'LTO'
AND cu.customer_no = '021540'
AND c.invoice_number IS NOT NULL
AND c.invoice_date IS NULL
AND c.invoice_number = '20170331'
SELECT Sum(price)
FROM replaced_part
WHERE claim_id IN ( 628099, 674047, 1182523, 1282549,
1479834, 1480585, 1487452, 1515238 );
SELECT Sum(price)
FROM allowance
WHERE claim_id IN ( 628099, 674047, 1182523, 1282549,
1479834, 1480585, 1487452, 1515238 );
I was trying to group together all the above queries into one using sql equi join as follows:
select co.company_Code,
cu.customer_No,
c.invoice_Number,
sum(r.price),
sum(a.price)
FROM claim c INNER JOIN company co ON c.COMPANY_ID=co.ID
INNER JOIN customer cu ON c.CUSTOMER_ID=cu.ID
INNER JOIN replaced_part r ON r.claim_id=c.id
INNER JOIN allowance a ON a.claim_id = c.id
WHERE co.company_code = 'LTO' and cu.customer_no='021540' and
c.INVOICE_NUMBER is not null and c.INVOICE_DATE is null and
c.INVOICE_NUMBER='20170331'
GROUP BY co.company_Code, cu.customer_No, c.invoice_Number
But I am not getting the desired result(Query execute successfully but the sum is incorrect) as I am getting after running the 3 separate queries defined above... What is the problem here in my query created using equi join??
Relationship between tables are claim ->(one to one) -> company ->(one to one) -> customer ->(one to many) ->Replaced_Part ->(one to many) -> allowance

Please try to use left join for Replaced_Part and allowance
select co.company_Code
, cu.customer_No
, c.invoice_Number
, (SELECT sum(r.price) FROM replaced_part r WHERE r.claim_id = c.id)
, (SELECT sum(a.price) FROM allowance a WHERE a.claim_id = c.id)
FROM claim c
INNER JOIN company co
ON c.COMPANY_ID=co.ID
INNER JOIN customer cu
ON c.CUSTOMER_ID=cu.ID
WHERE co.company_code = 'LTO'
AND cu.customer_no='021540'
AND c.INVOICE_DATE is null
AND c.INVOICE_NUMBER='20170331'
GROUP BY co.company_Code
, cu.customer_No
, c.invoice_Number

try to use temporary table, combine all the tables and make a temporary table, then fire your conditions on that table.
Example :
select co.company_Code,
cu.customer_No,
c.invoice_Number,
sum(r.price),
sum(a.price)
into #temp from (claim c
INNER JOIN company co ON c.COMPANY_ID=co.ID
INNER JOIN customer cu ON c.CUSTOMER_ID=cu.ID
INNER JOIN replaced_part r ON r.claim_id=c.id
INNER JOIN allowance a ON a.claim_id = c.id
and then use your conditions on #temp. like
select * from #temp
WHERE company_code = 'LTO' and customer_no='021540' and
INVOICE_NUMBER is not null and INVOICE_DATE is null and
INVOICE_NUMBER='20170331'
GROUP BY company_Code, customer_No, invoice_Number
Hope this will work for you.

Related

How to create distinct count from queries with several tables

I am trying to create one single query that will give me a distinct count for both the ActivityID and the CommentID. My query in MS Access looks like this:
SELECT
tbl_Category.Category, Count(tbl_Activity.ActivityID) AS CountOfActivityID,
Count(tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;
I know the answer must somehow include SELECT DISTINCT but am not able to get it to work. Do I need to create multiple subqueries?
This is really painful in MS Access. I think the following does what you want to do:
SELECT ac.Category, ac.num_activities, aco.num_comments
FROM (SELECT ca.category, COUNT(*) as num_activities
FROM (SELECT DISTINCT c.Category, a.ActivityID
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as caa
GROUP BY ca.category
) as ca LEFT JOIN
(SELECT c.Category, COUNT(*) as num_comments
FROM (SELECT DISTINCT c.Category, co.CommentId
FROM (tbl_Category as c INNER JOIN
tbl_Activity as a
ON c.CategoryID = a.CategoryID
) INNER JOIN
tbl_Comments as co
ON a.ActivityID = co.ActivityID
WHERE a.UnitID = 5 AND co.PeriodID = 1
) as aco
GROUP BY c.Category
) as aco
ON aco.CommentId = ac.CommentId
Note that your LEFT JOINs are superfluous because the WHERE clause turns them into INNER JOINs. This adjusts the logic for that purpose. The filtering is also very tricky, because it uses both tables, requiring that both subqueries have both JOINs.
You can use DISTINCT:
SELECT
tbl_Category.Category, Count(DISTINCT tbl_Activity.ActivityID) AS CountOfActivityID,
Count(DISTINCT tbl_Comments.CommentID) AS CountOfCommentID
FROM tbl_Category LEFT JOIN
(tbl_Activity LEFT JOIN tbl_Comments ON
tbl_Activity.ActivityID = tbl_Comments.ActivityID) ON
tbl_Category.CategoryID = tbl_Activity.CategoryID
WHERE
(((tbl_Activity.UnitID)=5) AND ((tbl_Comments.PeriodID)=1))
GROUP BY
tbl_Category.Category;

Access Subquery On mulitple conditions

This SQL query needs to be done in ACCESS.
I am trying to do a subquery on the total sales, but I want to link the sale to the province AND to product. The below query will work with one or the other: (po.product_name = allp.all_products) AND (p.province = allp.all_province); -- but it will no take both.
I will be including every month into this query, once I can figure out the subquery on with two criteria.
Select
p.province as [Province],
po.product_name as [Product],
all_price
FROM
(purchase_order po
INNER JOIN person p
on p.person_id = po.person_id)
left join
(
select
po1.product_name AS [all_products],
sum(pp1.price) AS [all_price],
p1.province AS [all_province]
from (purchase_order po1
INNER JOIN product pp1
on po1.product_name = pp1.product_name)
INNER JOIN person p1
on po1.person_id = p1.person_id
group by po1.product_name, pp1.price, p1.province
)
as allp
on (po.product_name = allp.all_products) AND (p.province = allp.all_province);
Make the first select sql into a table by giving it an alias and join table 1 to table 2. I don't have your table structure or data to test it but I think this will lead you down the right path:
select table1.*, table2.*
from
(Select
p.province as [Province],
po.product_name as [Product]
--removed this ,all_price
FROM
(purchase_order po
INNER JOIN person p
on p.person_id = po.person_id) table1
left join
(
select
po1.product_name AS [all_products],
sum(pp1.price) AS [all_price],
p1.province AS [all_province]
from (purchase_order po1
INNER JOIN product pp1
on po1.product_name = pp1.product_name)
INNER JOIN person p1
on po1.person_id = p1.person_id
group by po1.product_name, pp1.price, p1.province --check your group by, I dont think you want pp1.price here if you want to aggregate
) as table2 --changed from allp
on (table1.product = table2.all_products) AND (table1.province = table2.all_province);

SQL - SUM TotalValue returning a null with LEFT JOIN Clause

I'd to like SUM a TotalValue column based on Vendor and logged in user. I completely returning a right value of other info in logged user and the connected vendor, the problem is the SUM of TotalValue column is returning a null value. am I missing something?
This is what I've already tried:
SELECT ,v.VendorName ,
u.Product ,
v.[Description] ,
v.Status ,
SUM(cpm.TotalValue) AS TotalValue
FROM Vendor v
LEFT JOIN [ProductContract] c ON v.VendorId = c.VendorId
AND c.[Status] = 4
AND c.ProductContractId IN
(SELECT con.ProductContractId
FROM [ProductContract] con
INNER JOIN [ProductContractPermission] cp ON cp.ProductContractId = con.ProductContractId
WHERE cp.UserInfoId = #UserInfoId)
LEFT JOIN ProductContractPaymentMenu cpm ON c.ProductContractId = cpm.ProductContractId
AND c.[Status] = 4
AND c.VendorId = #VendorId
LEFT JOIN VendorContact vc ON v.VendorId = vc.VendorId
AND vc.[Type] = 1
LEFT JOIN UserInfo u ON vc.UserInfoId = u.UserInfoId
WHERE v.VendorId IN
(SELECT VendorId
FROM ClientVendor
WHERE ClientId = #VendorId)
GROUP BY v.VendorName,
u.Product,
v.[Description],
v.Status,
cpm.TotalValue
ORDER BY v.[Status],
v.CreatedOn
Seems that you want to apply an aggregate filter, this is the famous HAVING clause:
...
GROUP BY v.VendorName,
u.Product,
v.[Description],
v.Status,
cpm.TotalValue
HAVING
SUM(cpm.TotalValue) > 0
ORDER BY v.[Status],
v.CreatedOn
Hi i think you have to add ISNULL(value,defaultvalue) because cpm table was on left join and it can be null.
SELECT v.VendorName ,
u.Product ,
v.[Description] ,
v.Status ,
SUM(ISNULL(cpm.TotalValue,0)) AS TotalValue
FROM Vendor v
LEFT JOIN [ProductContract] c ON v.VendorId = c.VendorId
AND c.[Status] = 4
AND c.ProductContractId IN
(SELECT con.ProductContractId
FROM [ProductContract] con
INNER JOIN [ProductContractPermission] cp ON cp.ProductContractId = con.ProductContractId
WHERE cp.UserInfoId = #UserInfoId)
LEFT JOIN ProductContractPaymentMenu cpm ON c.ProductContractId = cpm.ProductContractId
AND c.[Status] = 4
AND c.VendorId = #VendorId
LEFT JOIN VendorContact vc ON v.VendorId = vc.VendorId
AND vc.[Type] = 1
LEFT JOIN UserInfo u ON vc.UserInfoId = u.UserInfoId
WHERE v.VendorId IN
(SELECT VendorId
FROM ClientVendor
WHERE ClientId = #VendorId)
GROUP BY v.VendorName,
u.Product,
v.[Description],
v.Status,
cpm.TotalValue
ORDER BY v.[Status],
v.CreatedOn
https://learn.microsoft.com/en-us/sql/t-sql/functions/isnull-transact-sql?view=sql-server-2017
Edit: other point you have miss ',' in your query after the SELECT.
Your left join on ProductContractPaymentMenu may not always get an item, so cpm.TotalValue can be NULL sometimes. When you use SUM and when one value is NULL then the result will be NULL. You might rewrite that part as:
SUM(ISNULL(cpm.TotalValue, 0)) AS TotalValue
In that case, it will treat non-existing records as value 0.

Oracle SQL How to Count Column Value Occurences and Group BY during joins

I'm working on another SQL query, trying to group a collection of records while doing a count and joining tables. See below for goal, current query, and attached scripts for building and populating tables.
Show all customers who have checked more books than DVDs. Display
customer name, total book checkouts and total DVD checkouts. Sort
results by customer first name and last name.
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, COUNT(T.TRANSACTION_ID)
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT ON P.CATALOG_ITEM_ID = CT.CATALOG_ITEM_ID
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
Run first: https://drive.google.com/open?id=1PYAZV4KIfZtxP4eQn35zsczySsxDM7ls
Run second: https://drive.google.com/open?id=1pAzWmJqvD3o3n6YJqVUM6TtxDafKGd3f
EDIT
With some help from Mr. Barbaros I've come up with the below query, which is closer. However, this query isn't returning any results for DVDs, which leads me to believe it's a join issue.
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, COUNT(CT1.TYPE) AS BOOK_COUNT, COUNT(CT2.TYPE) AS DVD_COUNT
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT1 ON P.CATALOG_ITEM_ID = CT1.CATALOG_ITEM_ID AND CT1.TYPE = 'BOOK'
LEFT OUTER JOIN catalog_item CT2 ON P.CATALOG_ITEM_ID = CT2.CATALOG_ITEM_ID AND CT2.TYPE = 'DVD'
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME, CT1.TYPE, CT2.TYPE
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
Use "conditional aggregates" (use a case expression inside the aggregate function)
SELECT
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
, COUNT( CASE WHEN CT.TYPE = 'BOOK' THEN T.TRANSACTION_ID END ) books
, COUNT( CASE WHEN CT.TYPE = 'DVD' THEN T.TRANSACTION_ID END ) dvds
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
INNER JOIN catalog_item CT ON P.CATALOG_ITEM_ID = CT.CATALOG_ITEM_ID
GROUP BY
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
HAVING
COUNT( CASE WHEN CT.TYPE = 'BOOK' THEN T.TRANSACTION_ID END )
> COUNT( CASE WHEN CT.TYPE = 'DVD' THEN T.TRANSACTION_ID END )
ORDER BY
C.CUSTOMER_FIRSTNAME
, C.CUSTOMER_LASTNAME
;
You can use catalog_item table twice( think of as seperate tables for books and dvds ), and compare by HAVING clause as :
SELECT C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME,
COUNT(CT1.CATALOG_ITEM_ID) as "Book Checkout",
COUNT(CT2.CATALOG_ITEM_ID) as "DVD Checkout"
FROM customer C
INNER JOIN library_card LC ON C.CUSTOMER_ID = LC.CUSTOMER_ID
INNER JOIN transaction T ON LC.LIBRARY_CARD_ID = T.LIBRARY_CARD_ID
INNER JOIN physical_item P ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
LEFT JOIN catalog_item CT1 ON P.CATALOG_ITEM_ID = CT1.CATALOG_ITEM_ID AND CT1.TYPE = 'BOOK'
LEFT JOIN catalog_item CT2 ON P.CATALOG_ITEM_ID = CT2.CATALOG_ITEM_ID AND CT1.TYPE = 'DVD'
GROUP BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME
HAVING COUNT(CT1.CATALOG_ITEM_ID) > COUNT(CT2.CATALOG_ITEM_ID)
ORDER BY C.CUSTOMER_FIRSTNAME, C.CUSTOMER_LASTNAME;
CUSTOMER_FIRSTNAME CUSTOMER_LASTNAME Book Checkout DVD Checkout
------------------ ----------------- ------------- -------------
Deena Pilgrim 3 1
Emile Cross 5 2
Please try to remove ,CT1.TYPE, CT2.TYPE on your group by clause.

Too many results in query

I'm fetching some data from our database in MSSQL. Out of this data I want to determine who created the client entry and who took the first payment from this client.
There can be many payment entries for a client on a single booking/enquiry and at the moment, my query shows results for each payment. How can I limit the output to only show the first payment entry?
My query:
SELECT
c.FirstName,
c.LastName,
c.PostalCode,
o.OriginOfEnquiry,
s.SuperOriginName,
c.DateOfCreation,
DATEDIFF(day, c.DateOfCreation, p.DateOfCreation) AS DaysToPayment,
pc.PackageName,
CONCAT(u.FirstName, ' ', u.LastName) AS CreateUser,
(SELECT CONCAT(u.FirstName, ' ', u.LastName)
WHERE u.UserID = p.UserID ) AS PaymentUser
FROM tblBookings b
INNER JOIN tblPayments p
ON b.BookingID = p.BookingID
INNER JOIN tblEnquiries e
ON e.EnquiryID = b.EnquiryID
INNER JOIN tblCustomers c
ON c.CustomerID = e.CustomerID
INNER JOIN tblOrigins o
ON o.OriginID = e.OriginID
INNER JOIN tblSuperOrigins s
ON s.SuperOriginID = o.SuperOriginID
INNER JOIN tblBookingPackages bp
ON bp.bookingID = p.BookingID
INNER JOIN tblPackages pc
ON pc.PackageID = bp.packageID
INNER JOIN tblUsers u
ON u.UserID = c.UserID
WHERE c.DateOfCreation >= '2016-06-01' AND c.DateOfCreation < '2016-06-30'
AND p.PaymentStatusID IN (1,2)
AND e.CustomerID = c.CustomerID
AND p.DeleteMark != 1
AND c.DeleteMark != 1
AND b.DeleteMark != 1
;
I tried adding a "TOP 1" to the nested select statement for PaymentUser, but it made no difference.
you can use cross apply with top 1:
FROM tblBookings b
cross apply
(select top 1 * from tblPayments p where b.BookingID = p.BookingID) as p
Instead of table tblPayments specify sub-query like this:
(SELECT TOP 1 BookingID, UserID, DateOfCreation
FROM tblPayments
WHERE DeleteMark != 1
AND PaymentStatusID IN (1,2)
ORDER BY DateOfCreation) as p
I'm assuming that tblPayments has a primary key column ID. If it is true, you can use this statment:
FROM tblBookings b
INNER JOIN tblPayments p ON p.ID = (
SELECT TOP 1 ID
FROM tblPayments
WHERE BookingID = b.BookingID
AND DeleteMark != 1
AND PaymentStatusID IN (1,2)
ORDER BY DateOfCreation)