SQL is it possible to use AND/OR with HAVING clause - sql-server

So I have these tables:
Products
--------
Product ID | Quantity
AND
OrdersLines
-----------
Product ID | Amount --(multiple lines with the same ID)
I'm using this select:
SELECT
P.ProductID,
P.Quantity,
SUM(OL.Amount) AS Ordered
FROM atbl_Sales_Products AS P
INNER JOIN atbl_Sales_OrdersLines AS OL ON OL.ProductID = P.ProductID
GROUP BY P.ProductID, P.Quantity
HAVING P.Quantity > SUM(OL.Amount)
The select works properly if ProductID is used in both tables.
However, if ProductID is not used in OrdersLines table or Amount in that table is Null - such rows are not included.

When you want to join across tables but always need to include records from one side of the join or the other, then you need to use one of the OUTER JOINs as opposed to the INNER JOIN in your SQL. If you want to include a record from your atbl_Sales_Products even when there may be no matching record in the atbl_Sales_OrderLines with the same ProductID then you should use a LEFT JOIN.
As mentioned in the comments you can use any operators you use in a WHERE clause with a HAVING clause.

SELECT
P.ProductID,
P.Quantity,
SUM(OL.Amount) AS Ordered
FROM atbl_Sales_Products AS P
LEFT JOIN atbl_Sales_OrdersLines AS OL
ON P.ProductID = OL.ProductID
GROUP BY P.ProductID, P.Quantity
HAVING SUM(OL.Amount) IS NULL
OR P.Quantity > SUM(Ol.Amount)
Additional OR statement solved my problem.

Related

SQL Query Multiple Joins Unexpected Results

My task is to write a query that will return sales information for each customer category and year. The columns required in the result set are:
OrderYear - the year the orders were placed
CustomerCategoryName - as it appears in the table Sales.CustomerCategories
CustomerCount - the number of unique customers placing orders for each CustomerCategoryName and OrderYear
OrderCount - the number of orders placed for each CustomerCategoryName and OrderYear
Sales - the subtotal from the orders placed, calculated from Quantity and UnitPrice of the table Sales.OrderLines
AverageSalesPerCustomer - the average sales per customer for each CustomerCategoryName and OrderYear
The results should be sorted in ascending order, first by order year, then by customer category name.
My attempt at a solution:
SELECT
CC.CustomerCategoryName,
YEAR(O.OrderDate) AS OrderYear,
COUNT(DISTINCT C.CustomerID) AS CustomerCount,
COUNT(DISTINCT O.OrderID) AS OrderCount,
SUM(OL.Quantity * OL.UnitPrice) AS Sales,
SUM(OL.Quantity * OL.UnitPrice) / COUNT(DISTINCT C.CustomerID) AS AverageSalesPerCustomer
FROM
Sales.CustomerCategories CC
INNER JOIN
Sales.Customers C ON C.CustomerCategoryID = CC.CustomerCategoryID
INNER JOIN
Sales.Orders O ON O.CustomerID = C.CustomerID
INNER JOIN
Sales.OrderLines OL ON OL.OrderID = O.OrderID
GROUP BY
CC.CustomerCategoryName, YEAR(O.OrderDate)
ORDER BY
YEAR(O.OrderDate), CC.CustomerCategoryName;
My OrderCount seems correct. However, I don't believe my CustomerCount is correct and my Sales and AverageSalesPerCustomer seem way off. The Categories that do not have any customers and orders do not show up in my results.
Is the reason that my counts are off and that he categories that do not have any customers are omitted is because they only have null values? I believe the question is looking for all the categories.
I am using the sample tables of WideWorldImporters from Microsoft.
Any help would be appreciated as I am new to SQL and Joins are a very hard concept for me to understand.
Presently, you're getting only the data that exists in order details...and not getting anything for the non-existent orders. Normally, this is accomplished with outer joins instead of inner joins, and an isnull(possiblyNullValue,replacementValue).
Also, while you're grouping by year(o.OrderDate), your join for orders isn't distinguishing by year...probably getting all years worth of data for each customer for each reporting period.
So, let's get the reporting period out first...and make sure we're basing our results on that:
select distinct year(o.OrderDate) from Sales.Orders
But really, you want all categories and all years...so you can combine them to get the real basis:
select
cc.CustomerCategoryId,
cc.CustomerCategoryName,
year(o.OrderDate)
from
Sales.Orders o
cross join
Sales.CustomerCategories cc
group by
cc.CustomerCategoryId,
cc.CustomerCategoryName,
year(o.OrderDate)
Now, you want to join this mess into the remaining query. There are two ways to do this...one is to use a with clause...but sometimes it's just easier to just wrap the basis query up in parentheses and use it as if it was a table:
select
cy.CustomerCategoryName,
cy.CalendarYear,
count(distinct c.CustomerId) CustomerCount,
isnull(sum(ol.UnitPrice * ol.Quantitiy),0.0) Sales,
isnull(sum(ol.UnitPrice * ol.Quantitiy) / count(distinct c.CustomerId),0.0) AverageSalesPerCustomer
from
(
select
cc.CustomerCategoryId,
cc.CustomerCategoryName,
year(o.OrderDate) CalendarYear --> must name calc'd cols in virtual tables
from
Sales.Orders o
cross join
Sales.CustomerCategories cc
group by
cc.CustomerCategoryId,
cc.CustomerCategoryName,
year(o.OrderDate)
) as cy --> cy is the "Category Years" virtual table
left outer join
Sales.Customers c
on cy.CustomerCategoryId = c.CustomerCategoryId
left outer join
Sales.Orders o
on
c.CustomerId = o.CustomerId --> join on customer and year
and --> to make sure we're only getting
cy.CalendarYear = Year(o.OrderDate) --> orders in the right year
left outer join
Sales.OrderLines ol
on o.OrderId = ol.OrderId
group by
cy.CalendarYear,
cy.CustomerCategoryName
order by
cy.CalendarYear,
cy.CustomerCategoryName
By the way...get comfortable messing with your queries to select some subset...for example, you can add a where clause to select only one company...and then go have a look at the details...to see if it passes the smell test. It's a lot easier to evaluate the results when you limit them. Similarly, you can add the customer to the select list and the outer grouping for the same reason. Experimentation is the key.

Grouping data based on expression using CountDistinct aggregate function

I am newbie to Stack overflow and also SQL server reporting services. So please excuse me for the format of the question.
So here is the situation:
I am developing a SSRS report which needs to be grouped by an Count of Distinct product names as shown below.
I created a text box called ProdCount with an expression
COUNTDISTNCT(Fields!Product.value,"DataSet1")
which gives me the count 63 within the scope of DataSet1.
Now i need to group the data by taking product names where the above formula is >1 .
=IIF(ProdCount>1,Fields!Product.value,Nothing)
My Problem:
I tried to call the ProdCount from the calculated field since i
cant use the aggregate functions in Calculated Fields and use
the second expression by using
= ReportItems!ProdCount.value
which gives me an error FieldValue Denying ReportItems
I tried to combine the above two expressions by creating a calculated field by
IIF(CountDistinct(Fields!Product.Value,"DataSet1")>1,Fields!Product.Value,Nothing)
which gives me an error Calculated fields cannot have expressions
I tried to use Report Variables in the same way as above(1) which was not working either.
I also tried to use CROSS JOIN in the query
Select Count(Distinct(Product Name)
from Query1
Cross join
My Main Query which give me the data
which is taking more time to execute.
So Can anyone help me with solution where i can group the data by combining the above two expressions.
Please excuse me for the format. I was confused with framing question. I accept all your edits , so that i can learn in future.
Here is my code:
SELECT * FROM
--Query1 which counts the number of distinct products)
(SELECT DISTINCT COUNT(gproduct.ProductName) AS ProdCount
FROM Table1
LEFT JOIN Table4
ON Table1.column=Table1.column
LEFT JOIN Table2
ON Table3.Column = TTable1.Column
LEFT JOIN
(
SELECT Distinct Table6.Name AS ProductName,Table9.ColumnId
FROM Table6
INNER JOIN Table7
ON Table6.Column=Table7.Column
INNER JOIN Table8
ON Table7.Column=Table8.Column
INNER JOIN Table9
ON Table9.Column=Table8.Column
)gproduct
ON Table1.ColumnId=gproduct.ColumnId
GROUP BY gproduct.ColumnId,
)qProduct
CROSS JOIN
--My main Query which get data from different table including Product name
(SELECT
Upper(CASE WHEN (CASE WHEN Table4.Column =1 THEN 'Yes' ELSE 'NO' END)='YES'
THEN gab.ProductName
ELSE
Table2.productName
END) AS Product,
FROM Table1 AS ec
LEFT JOIN Table2 AS ep
ON --
LEFT JOIN Table3 AS ebrd
ON --
Left JOIN Table4 AS etpc
ON --
LEFT JOIN Table5 AS gst
ON --
LEFT JOIN
(
SELECT Distinct Table6.Name AS ProductName,Table9.ColumnId
FROM Table6
INNER JOIN Table7
ON Table6.Column=Table7.Column
INNER JOIN Table8
ON Table7.Column=Table8.Column
INNER JOIN Table9
ON Table9.Column=Table8.Column
) gab
ON Table1.ColumnId=gab.ColumnId
)QMain
Personally I would try to solve the problem in query itself instead of SSRS report. According the data you provided it would be something like:
SELECT
ProductName,
count(distinct Product)
from
YourTable
group by
ProductName
having count(distinct product) > 1
Later on creating SSRS report should be quite easy.

How do I build a query that crosses multiple tables?

Here are my tables:
CUSTOMER
Cust_ID (PK)
Name
ORDERS
Order_ID (PK)
Cust_ID (FK)
ORDER_LINE
Order_ID (pk)
Part_ID (FK)
PART
Part_ID (PK)
Part_Description
Now I want to list the customer details, the part number and the description of the parts that each customer ordered.
How do i do this?
Thanks.
You should use "JOIN" using the FK, but from what I see you don't have a foreign key between "ORDERS" and "ORDER_LINE". Are you sure you're not missing something from the table definition, ie: ORDER_LINE should maybe have the ORDER_ID as a FK ?
Hope this helps
You can try something like
SELECT c.*,
p.*
FROM CUSTOMER c INNER JOIN
ORDERS o ON c.Cust_ID = o.Cust_ID INNER JOIN
ORDER_LINE ol ON o.Order_ID = ol.Order_Number INNER JOIN
PART p ON ol.Part_Number = p.Part_Number
Have a look at
Join (SQL)
An SQL join clause combines records from two or more tables in a
database.
SQL Joins
The JOIN keyword is used in an SQL statement to query data from two or
more tables, based on a relationship between certain columns in these
tables.
And for some graphic examples
JOIN Basics
What you need is a simple straightforward JOIN like so:
SELECT
c.Cust_ID,
c.Name,
l.Part_Number,
l.Part_Description
FROM CUSTOMER c
INNER JOIN ORDERS o ON c.Cust_ID = o.Cust_ID
INNER JOIN ORDER_LINE ol ON o.OrdeR_ID = ol.Order_Number
INNER JOIN PART l ON ol.Part_Number = l.Part_Number
You want an SQL "join", such as:
SELECT c.Name, ol.Part_Number, p.Part_Description
FROM Customer AS c
JOIN Orders AS o ON c.Cust_ID = o.Cust_ID
JOIN Order_Line AS ol ON o.Order_ID = ol.Order_Number
JOIN Part AS p ON ol.Part_Number = p.Part_Number
Be aware that without a WHERE clause, this query will return all all parts in all orders for all customers, which will really hammer the network and perform poorly on anything but a tiny database:
WHERE (c.Cust_ID = MyCustomerID)
MySQL join syntax
SQL Server join syntax

preventing display of duplicate records in SQL server

I'm using an stored procedure in SQL server, but it is giving me some duplicate records, of course I don't have duplicate records in my database, but my stored procedure is giving me two instances of a same record, what can be wrong? how can I prevent my query from giving duplicate records?
it is my SP select clause:
select (ROW_NUMBER() OVER (ORDER BY Review.Point desc) ) as rownumber,
Business.BusinessId,Business.BName,Business.BAddress1
,Business.BAddress2,Business.BCity,Business.BState,Business.BZipCode,Business.countryCode,Business.BPhone1,Business.BPhone2,Business.BEmail,Business.Keyword
,Business.BWebAddress,Business.BCatId,Business.BSubCatId,Business.BDetail,Business.bImage,Business.UCId,Business.UCConfirm
,Business.UOId,Business.UOConfirm,Business.x,Business.y,Cat.CatName,SubCat1.SubCatName
from Business left outer join
Review on business.BusinessId=Review.BusinessId left outer join
Cat on business.BCatid=Cat.CatId left outer join
SubCat1 on business.BSubCatid=SubCat1.SubCatId '+#sql2+'
) as tbl
where rownumber between '+CONVERT(varchar, #lbound)+' and '+CONVERT(varchar, #ubound);
I don't know your data to dig in to your join logic, but if it duplicating across BusinessID, you could add another ROW_NUMBER() for the duplicates:
select (ROW_NUMBER() OVER (ORDER BY Review.Point desc) ) as rownumber,
r = ROW_NUMBER()OVER(PARTITION BY Business.BusinessId ORDER BY Business.BusinessId)
Business.BusinessId,Business.BName,Business.BAddress1
,Business.BAddress2,Business.BCity,Business.BState,Business.BZipCode,Business.countryCode,Business.BPhone1,Business.BPhone2,Business.BEmail,Business.Keyword
,Business.BWebAddress,Business.BCatId,Business.BSubCatId,Business.BDetail,Business.bImage,Business.UCId,Business.UCConfirm
,Business.UOId,Business.UOConfirm,Business.x,Business.y,Cat.CatName,SubCat1.SubCatName
from Business left outer join
Review on business.BusinessId=Review.BusinessId left outer join
Cat on business.BCatid=Cat.CatId left outer join
SubCat1 on business.BSubCatid=SubCat1.SubCatId '+#sql2+'
) as tbl
where rownumber between '+CONVERT(varchar, #lbound)+' and '+CONVERT(varchar, #ubound)
AND r = 1;
Include the reserved word DISTINCT in your query.
eg
select distinct
*
from
students s
inner join enrollments e on e.StudentId = s.Id
inner join courses c on c.Id = e.CourseId
However, unexpected duplicates in a result table is often (but not always) a clue that you have a badly formed query or a badly designed database.
Try to remove this left join
Review on business.BusinessId=Review.BusinessId left outer join
seems not needed in your query and if there are more than one review for one business ...

Count of operations in group by

Recently in the code of my collegue I saw an sql query, where she used GROUP BY with lots of columns. Most of these columns needn't be grouped in the query. She has done this to prevent this error:
Column 'some_col' is invalid in the select list because it is
not contained in either an aggregate function or the GROUP BY clause.
I was wondering how heavy GROUP BY is, and is it ok to use such statements? If it is heavy than I'd better optimize her the query cause now I work on that piece of code.
It is hard to tell for sure without seeing the specific query, but I used to achieve surprising performance gains (at leas in SQL2K) by minimizing number of columns included in GROUP BY, and resolving those columns back with join on the inner query. To be more specific: let's assume you have classing OrderDetails (OrderID, ProductID, Quantity, Price) and Products (ProductID, ProductName) tables. Changing this query:
select P.ProductID, ProductName, sum(Quantity * Price)
from Products as P
inner join OrderDetails as OD on P.ProductID = OD.ProductID
group by P.ProductID, ProductName
to this:
select X.ProductID, PP.ProductName, X.OrderValue
from
(
select P.ProductID, sum(Quantity * Price) as OrderValue
from Products as P
inner join OrderDetails as OD on P.ProductID = OD.ProductID
group by P.ProductID
) as X
inner join Products as PP on X.ProductID = P.ProductID
would give me performance gain despite two joins to the same table, because grouping on integer index was faster then grouping on text-valued, unsorted product name.

Resources