Specifying a column to compare in HAVING subquery - sql

I am trying to compare sales from different quarters by stores in adventureworks 2014.
My code is as following
SELECT store.BusinessEntityID as "StoreID", "Name"
from sales.SalesOrderHeader
inner join sales.customer
on sales.SalesOrderHeader.CustomerID = sales.customer.CustomerID
inner join sales.store
on sales.customer.StoreID= sales.store.BusinessEntityID
where (OrderDate between '2014-01-01' and '2014-03-31')
and (OnlineOrderFlag = 0)
group by store.BusinessEntityID, "Name"
having format(round(sum(subtotal),2),'###,###,###.##') > format(round(sum(subtotal),2),'###,###,###.##') IN
(
SELECT store.BusinessEntityID as "StoreID",
format(round(sum(subtotal),2),'###,###,###.##') "Ventes2013_Q4"
from sales.SalesOrderHeader
inner join sales.customer on sales.SalesOrderHeader.CustomerID = sales.customer.CustomerID
inner join sales.store on sales.customer.StoreID= sales.store.BusinessEntityID
where (orderdate between '2013-10-01' and '2013-12-31') and OnlineOrderFlag = 0
group by store.BusinessEntityID
)
Both my main query and my subquery work individually, but I am not able to specify the column i want to compare it with in the second part of the having. When trying IN it gives me an incorrect syntax, and it does not work when using the column name either. It also did not work when adding a sales column in the main query
Can anyone point me in the right direction?

This is a simple way to do it (translating from your code)
SELECT StoreID, Name
FROM (
SELECT store.BusinessEntityID as "StoreID", "Name", sum(stubtotal) as sum
from sales.SalesOrderHeader
inner join sales.customer on sales.SalesOrderHeader.CustomerID = sales.customer.CustomerID
inner join sales.store on sales.customer.StoreID= sales.store.BusinessEntityID
where (OrderDate between '2014-01-01' and '2014-03-31') and (OnlineOrderFlag = 0)
group by store.BusinessEntityID, "Name"
) first
JOIN (
SELECT BusinessEntityID, sum(subtotal) as sum
from sales.SalesOrderHeader
inner join sales.customer on sales.SalesOrderHeader.CustomerID = sales.customer.CustomerID
inner join sales.store on sales.customer.StoreID= sales.store.BusinessEntityID
where (orderdate between '2013-10-01' and '2013-12-31') and OnlineOrderFlag = 0
group by store.BusinessEntityID
) as sub on sub.BusinessEntityID = furst.BusinessEntityID
AND sub.sum < first.sum
However there is a better way in sql server... one sec.
This will give to you totals by quarter and store:
SELECT
store.BusinessEntityID as StoreID,
Name,
year(OrderDate) as y,
quarter(orderdate) as q,
sum(stubtotal) as sum
from sales.SalesOrderHeader
inner join sales.customer on sales.SalesOrderHeader.CustomerID = sales.customer.CustomerID
inner join sales.store on sales.customer.StoreID= sales.store.BusinessEntityID
group by store.BusinessEntityID, Name, year(OrderDate), quarter(orderdate)
Now create a view
CREATE VIEW quarterview as
SELECT
store.BusinessEntityID as StoreID,
Name,
year(OrderDate) as y,
quarter(orderdate) as q,
sum(stubtotal) as sum
from sales.SalesOrderHeader
inner join sales.customer on sales.SalesOrderHeader.CustomerID = sales.customer.CustomerID
inner join sales.store on sales.customer.StoreID= sales.store.BusinessEntityID
group by store.BusinessEntityID, Name, year(OrderDate), quarter(orderdate)
"I want to show the stores that have more sales in 2014Q1 than in 2013Q4"
SELECT *
FROM quarterview first
WHERE q = 1 and year = 2014
then
SELECT *
FROM quarterview first
JOIN quarterview second ON second.q = 4 and second.year=2013 and first.sum > second.sum
WHERE first.q = 1 and first.year = 2014
With your fab new quarter view all the client request are easy to do with just a couple of lines of code.

With Ventes2014_Q1 as(
SELECT store.BusinessEntityID as "StoreID", "Name",format(round(sum(subtotal),2),'###,###,###.##') "Ventes2014_Q1"
from sales.SalesOrderHeader
inner join sales.customer
on sales.SalesOrderHeader.CustomerID = sales.customer.CustomerID
inner join sales.store
on sales.customer.StoreID= sales.store.BusinessEntityID
where (OrderDate between '2014-01-01' and '2014-03-31')
and (OnlineOrderFlag = 0)
group by store.BusinessEntityID, "Name"
),Ventes2013_Q4
(
SELECT store.BusinessEntityID as "StoreID",
format(round(sum(subtotal),2),'###,###,###.##') "Ventes2013_Q4"
from sales.SalesOrderHeader
inner join sales.customer on sales.SalesOrderHeader.CustomerID = sales.customer.CustomerID
inner join sales.store on sales.customer.StoreID= sales.store.BusinessEntityID
where (orderdate between '2013-10-01' and '2013-12-31') and OnlineOrderFlag = 0
group by store.BusinessEntityID
)
select "StoreID", "Name" from Ventes2014_Q1
where "StoreID" exists
(select "StoreID" from Ventes2013_Q4 where Ventes2013_Q4."StoreID"=Ventes2014_Q1."StoreID" and Ventes2014_Q1."Ventes2014_Q1">Ventes2013_Q4."Ventes2013_Q4")

Related

How can I rotate a table that requires a group by without using pivot and case when in sql?

This is the code I'm trying to run
select min(sod.ModifiedDate) as [ModifiedDate]
,Bikes = (select sum(LineTotal) from SalesOrderDetail where max(ProductCategoryName) = 'Bikes' and ModifiedDate = sod.ModifiedDatee)
,Components = (select sum(LineTotal) from SalesOrderDetail where max(ProductCategoryName) = 'Components' and ModifiedDate = sod.ModifiedDate)
,Clothing = (select sum(LineTotal) from SalesOrderDetail where max(ProductCategoryName) = 'Clothing' and ModifiedDate = sod.ModifiedDate )
,Accessories = (select sum(LineTotal) from SalesOrderDetail where max(ProductCategoryName) = 'Accessories' and ModifiedDate = sod.ModifiedDate )
from SalesOrderDetail sod
inner join product p on p.ProductID = sod.ProductID
inner join ProductSubcategory ps on ps.ProductSubcategoryID = p.ProductSubcategoryID
inner join ProductCategory pc on pc.ProductCategoryID = ps.ProductCategoryID
group by ProductCategoryName
,sod.ModifiedDate
,datepart(year, sod.ModifiedDate)
,datepart(month, sod.ModifiedDate)
,datepart(day, sod.ModifiedDate)
order by datepart(year, sod.ModifiedDate)
,datepart(month, sod.ModifiedDate)
,datepart(day, sod.ModifiedDate)
I can't figure out how to make it so it splits the LineTotal into the four ProductNameCategory like this: (expected result)
ModifiedDate
Bikes
Components
Clothing
Accessories
2005-07-01 00:00:00.000
467709.136900
31525.960400
2875.153600
1695.666000
2005-07-02 00:00:00.000
13931.520000
NULL
NULL
NULL
2005-07-03 00:00:00.000
15012.178200
NULL
NULL
NULL
2005-07-04 00:00:00.000
7156.540000
NULL
NULL
NULL
2005-07-05 00:00:00.000
15012.178200
NULL
NULL
NULL
All I get is this, it adds all the lineTotal for a given date regardless of ProductCategoryName and then puts the sum in Components, except when the only thing there is that day is Bikes, then he puts it in Bikes.
ModifiedDate
Bikes
Components
Clothing
Accessories
2005-07-01 00:00:00.000
NULL
503805.916900
NULL
NULL
2005-07-02 00:00:00.000
13931.520000
NULL
NULL
NULL
2005-07-03 00:00:00.000
15012.178200
NULL
NULL
NULL
2005-07-04 00:00:00.000
7156.540000
NULL
NULL
NULL
2005-07-05 00:00:00.000
15012.178200
NULL
NULL
NULL
How can I make it look like the expected result without Pivot and case when? I need to get the results showed here using four different methods to then test performance and I already used pivot and case when. I'm trying to use this method I found https://learn.microsoft.com/en-us/troubleshoot/sql/database-design/rotate-table for this specific query
Here's one way to do it without PIVOT or CASE, but it's really ugly as a result, even after pulling some of the joins and aggregations out of the main query:
WITH ProductMeta AS
(
SELECT p.ProductID, CatName = pc.Name
FROM Production.Product AS p
INNER JOIN Production.ProductSubcategory ps
on ps.ProductSubcategoryID = p.ProductSubcategoryID
INNER JOIN Production.ProductCategory pc
on pc.ProductCategoryID = ps.ProductCategoryID
WHERE pc.Name IN (N'Bikes',N'Components',N'Clothing',N'Accessories')
), Agg AS
(
SELECT date = CONVERT(date, sod.ModifiedDate),
pm.CatName,
LineTotal = SUM(LineTotal)
FROM Sales.SalesOrderDetail sod
INNER JOIN ProductMeta AS pm ON sod.ProductID = pm.ProductID
GROUP BY CONVERT(date, sod.ModifiedDate), pm.CatName
)
SELECT a.date
,Bikes = (SELECT SUM(LineTotal) FROM Agg
WHERE date = a.date AND CatName = 'Bikes')
,Components = (SELECT SUM(LineTotal) FROM Agg
WHERE date = a.date AND CatName = 'Components')
,Clothing = (SELECT SUM(LineTotal) FROM Agg
WHERE date = a.date AND CatName = 'Clothing')
,Accessories = (SELECT SUM(LineTotal) FROM Agg
WHERE date = a.date AND CatName = 'Accessories')
FROM Agg AS a
GROUP BY a.date;
This is what I came up with just modifying your code. After downloading AdventureWorks, I validated this runs and the output matches your table above (minus the superfluous 00:00:00 times);
select DISTINCT CAST(sod.ModifiedDate as date) as [ModifiedDate]
, Bikes = (
select sum(LineTotal)
from Sales.SalesOrderDetail as sod2
inner join production.product p on p.ProductID = sod2.ProductID
inner join production.ProductSubcategory ps on ps.ProductSubcategoryID = p.ProductSubcategoryID
inner join production.ProductCategory pc on pc.ProductCategoryID = ps.ProductCategoryID
where pc.[Name] = 'Bikes'
and CAST(sod2.ModifiedDate as date) = CAST(sod.ModifiedDate as date)
)
, Components = (
select sum(LineTotal)
from Sales.SalesOrderDetail as sod2
inner join production.product p on p.ProductID = sod2.ProductID
inner join production.ProductSubcategory ps on ps.ProductSubcategoryID = p.ProductSubcategoryID
inner join production.ProductCategory pc on pc.ProductCategoryID = ps.ProductCategoryID
where pc.[Name] = 'Components'
and CAST(sod2.ModifiedDate as date) = CAST(sod.ModifiedDate as date)
)
, Clothing = (
select sum(LineTotal)
from Sales.SalesOrderDetail as sod2
inner join production.product p on p.ProductID = sod2.ProductID
inner join production.ProductSubcategory ps on ps.ProductSubcategoryID = p.ProductSubcategoryID
inner join production.ProductCategory pc on pc.ProductCategoryID = ps.ProductCategoryID
where pc.[Name] = 'Clothing'
and CAST(sod2.ModifiedDate as date) = CAST(sod.ModifiedDate as date)
)
, Accessories = (
select sum(LineTotal)
from Sales.SalesOrderDetail as sod2
inner join production.product p on p.ProductID = sod2.ProductID
inner join production.ProductSubcategory ps on ps.ProductSubcategoryID = p.ProductSubcategoryID
inner join production.ProductCategory pc on pc.ProductCategoryID = ps.ProductCategoryID
where pc.[Name] = 'Accessories'
and CAST(sod2.ModifiedDate as date) = CAST(sod.ModifiedDate as date)
)
from Sales.SalesOrderDetail sod
order by CAST(sod.modifiedDate as date)
Sample results:
EDIT:
This query completes in less than 0.359 seconds.
WITH prelim as (
select sod2.LineTotal, sod2.ModifiedDate, pc.Name as ProductCategoryName
from Sales.SalesOrderDetail as sod2
inner join production.product p on p.ProductID = sod2.ProductID
inner join production.ProductSubcategory ps on ps.ProductSubcategoryID = p.ProductSubcategoryID
inner join production.ProductCategory pc on pc.ProductCategoryID = ps.ProductCategoryID
)
select DISTINCT CAST(sod.ModifiedDate as date) as [ModifiedDate]
, Bikes = (
SELECT SUM(LineTotal)
FROM prelim as p
WHERE p.ModifiedDate = sod.ModifiedDate
AND p.ProductCategoryName = 'Bikes'
)
, Components = (
SELECT SUM(LineTotal)
FROM prelim as p
WHERE p.ModifiedDate = sod.ModifiedDate
AND p.ProductCategoryName = 'Components'
)
, Clothing = (
SELECT SUM(LineTotal)
FROM prelim as p
WHERE p.ModifiedDate = sod.ModifiedDate
AND p.ProductCategoryName = 'Clothing'
)
, Accessories = (
SELECT SUM(LineTotal)
FROM prelim as p
WHERE p.ModifiedDate = sod.ModifiedDate
AND p.ProductCategoryName = 'Accessories'
)
from Sales.SalesOrderDetail sod
order by CAST(sod.modifiedDate as date)

T-SQL Programming Return table based on Variable

I've been trying to make this work for a while now but I can't figure what's wrong.
The function needs to return a table based on the input date. If the input year is not available in the database, the function needs to return the data for all the years, otherwise, it needs to return the data only for the specified year.
Maybe someone here can help.
I'm using the 2014 AdventureWorks Database.
Thanks
CREATE FUNCTION DBO.udf_fonction(#year INT)
RETURNS TABLE
AS
RETURN
IF #year IN (SELECT DISTINCT(YEAR(OrderDate)) AS Years
FROM Sales.SalesOrderHeader)
(
SELECT YEAR(A.OrderDate) AS SalesYear,
C.Name AS SalesTerritory,
D.Name AS SalesCountryName,
CASE(A.OnlineOrderFlag)
WHEN 1 THEN 'Online'
WHEN 0 THEN 'In-store'
END AS SalesType,
SUM(A.SubTotal) AS Montant
FROM sales.SalesOrderHeader A
INNER JOIN Sales.Customer B ON A.CustomerID = B.CustomerID
INNER JOIN Sales.SalesTerritory C ON B.TerritoryID = C.TerritoryID
INNER JOIN Person.CountryRegion D ON C.CountryRegionCode = D.CountryRegionCode
WHERE YEAR(A.OrderDate) = #year
GROUP BY YEAR(A.OrderDate), C.Name, D.Name, CASE(A.OnlineOrderFlag)
WHEN 1 THEN 'Online'
WHEN 0 THEN 'In-store'
END
)
ELSE (SELECT YEAR(A.OrderDate) AS SalesYear,
C.Name AS SalesTerritory,
D.Name AS SalesCountryName,
CASE(A.OnlineOrderFlag)
WHEN 1 THEN 'Online'
WHEN 0 THEN 'In-store'
END AS SalesType,
SUM(A.SubTotal) AS Montant
FROM sales.SalesOrderHeader A
INNER JOIN Sales.Customer B ON A.CustomerID = B.CustomerID
INNER JOIN Sales.SalesTerritory C ON B.TerritoryID = C.TerritoryID
INNER JOIN Person.CountryRegion D ON C.CountryRegionCode = D.CountryRegionCode
GROUP BY YEAR(A.OrderDate), C.Name, D.Name, CASE(A.OnlineOrderFlag)
WHEN 1 THEN 'Online'
WHEN 0 THEN 'In-store'
END
)
GO
WHERE YEAR(A.OrderDate) = #year
OR not exists (select 1 from sales.SalesOrderHeader where YEAR(OrderDate) = #year)
You are not returning a table anywhere

Finding the count

I have the following SQL query and need to know the count of companyid as I can see repeating data. How do I find the count of it. Following is the query
SELECT a.companyId 'companyId'
, i.orgDebtType 'orgDebtType'
, d.ratingTypeName 'ratingTypeName'
, c.currentRatingSymbol 'currentRatingSymbol'
, c.ratingStatusIndicator 'ratingStatusIndicator'
, g.qualifierValue 'qualifierValue'
, c.ratingdate 'ratingDate'
, h.value 'outlook'
FROM ciqRatingEntity a
JOIN ciqcompany com
on com.companyId = a.companyId
JOIN ciqratingobjectdetail b ON a.entitySymbolValue = b.objectSymbolValue
JOIN ciqRatingData c ON b.ratingObjectKey = c.ratingObjectKey
JOIN ciqRatingType d ON b.ratingTypeId = d.ratingTypeId
JOIN ciqRatingOrgDebtType i ON i.orgDebtTypeId=b.orgDebtTypeId
JOIN ciqRatingEntityData red ON red.entitySymbolValue=a.entitySymbolValue
AND red.ratingDataItemId='1' ---CoName
LEFT JOIN ciqRatingDataToQualifier f ON f.ratingDataId = c.ratingDataId
LEFT JOIN ciqRatingQualifiervalueType g ON g.qualifiervalueid = f.qualifierValueId
LEFT JOIN ciqRatingValueType h ON h.ratingValueId = c.outlookValueId
WHERE 1=1
AND b.ratingTypeId IN ( '130', '131', '126', '254' )
-- and a.companyId = #companyId
AND a.companyId IN
(SELECT distinct TOP 2000000
c.companyId
FROM ciqCompany c
inner join ciqCompanyStatusType cst on cst.companystatustypeid = c.companystatustypeid
inner join ciqCompanyType ct on ct.companyTypeId = c.companyTypeId
inner join refReportingTemplateType rep on rep.templateTypeId = c.reportingtemplateTypeId
inner join refCountryGeo rcg on c.countryId = rcg.countryId
inner join refState rs on rs.stateId = c.stateId
inner join ciqSimpleIndustry sc on sc.simpleIndustryId = c.simpleIndustryId
ORDER BY companyid desc)
ORDER BY companyId DESC, c.ratingdate, b.ratingTypeId, c.ratingStatusIndicator
This will list where there are duplicate companyID's
SELECT companyId, count(*) as Recs
FROM ciqCompany
GROUP BY ciqCompany
HAVING count(*) > 1
I understand that you wish to add a column to the query with the count of each companyId, you can use COUNT() OVER():
select count(a.companyId) over (partition by a.companyId) as companyCount,
<rest of the columns>
from ciqRatingEntity a
join <rest of the query>
This would return in each row the count of the companyId of that row without grouping the results.

Identical aggregate-function for different WHERE's

Im stuck atm. Trying to figure out how to use an aggregate function like SUM(column1*column2) for making results in multiple columns. I want to print ex. SUM(qty*unitprice) where orderdate between "blabla" and "blabla",
and then i want another column which uses the same function(sum(qty*unitprice)
but with additional expressions in the where clause. this is my code example, it doesnt show anything:
select Orderdates, Sales, SalesDisc
FROM ( select month(OH.OrderDate) as Orderdates, sum(OD.OrderQty*OD.UnitPrice) as Sales
from sales.SalesOrderDetail OD
inner join sales.salesorderheader OH on
OD.SalesOrderID = OH.SalesOrderID
where OrderDate >= ('2014-01-01') and OrderDate < ('2015-01-01')
group by month(OH.orderdate)
) A
Join
(select month(OH.OrderDate) as orderdatez, sum(OD.OrderQty*OD.UnitPrice) as SalesDisc
from sales.SalesOrderDetail OD
inner join sales.SalesOrderHeader OH on
OD.SalesOrderID = OH.SalesOrderID
where OrderDate >= ('2014-01-01') and OrderDate < ('2015-01-01')
and OD.SpecialOfferID between 2 and 16
Group by Month(OH.orderdate)
) B
on A.Orderdates = B.orderdatez and A.Sales = B.SalesDisc
If am not wrong this is what you need
SELECT Month(OH.OrderDate) AS Orderdates,
Sum(OD.OrderQty * OD.UnitPrice) AS Sales,
Sum(CASE
WHEN OD.SpecialOfferID BETWEEN 2 AND 16 THEN ( OD.OrderQty * OD.UnitPrice )
ELSE 0
END) AS SalesDisc
FROM sales.SalesOrderDetail OD
INNER JOIN sales.salesorderheader OH
ON OD.SalesOrderID = OH.SalesOrderID
WHERE OrderDate >= ( '2014-01-01' )
AND OrderDate < ( '2015-01-01' )
GROUP BY Month(OH.orderdate)
Note : It will be more meaningful if you add year(OH.orderdate) in group by

Count from another table with join

How can I Count the Lending comments for each lending (the comments is on another table called "LendingComments" with a reference Column called "LendingId" ?
SELECT LendingStatus.Status, Products.Productname, Products.Serial_number, Deposits.Amount, Lendings.DeliveryDate, Lendings.Id AS LendingId, Products.Id AS ProductId FROM Lendings
LEFT JOIN Products ON Lendings.ProductId = Products.Id
LEFT JOIN LendingStatus ON Lendings.StatusId = LendingStatus.Id
LEFT JOIN Deposits ON Lendings.DepositId = Deposits.Id
WHERE PersonId = 561 ORDER BY DeliveryDate DESC
Maby like this (if I understand the question well enough)
SELECT
LendingStatus.Status, Products.Productname, Products.Serial_number,Deposits.Amount, Lendings.DeliveryDate, Lendings.Id AS LendingId, Products.Id AS ProductId, LendingComments.NumLendingComments
FROM Lendings
LEFT JOIN Products ON Lendings.ProductId = Products.Id
LEFT JOIN LendingStatus ON Lendings.StatusId = LendingStatus.Id
LEFT JOIN Deposits ON Lendings.DepositId = Deposits.Id
OUTER APPLY
(
SELECT
COUNT(*) AS NumLendingComments
FROM
LendingComments PL
WHERE
PL.LendingID = Lendings.LendingID
) AS LendingComments WHERE Personid = 561 ORDER BY DeliveryDate desc
Maybe this helps:
SELECT CommentCount = Sum(lc.comments)
OVER (
partition BY lc.id),
lendingstatus.status,
products.productname,
products.serial_number,
deposits.amount,
lendings.deliverydate,
lendings.id AS LendingId,
products.id AS ProductId
FROM lendings
LEFT JOIN products
ON lendings.productid = products.id
LEFT JOIN lendingstatus
ON lendings.statusid = lendingstatus.id
LEFT JOIN deposits
ON lendings.depositid = deposits.id
LEFT JOIN LendingComments lc
ON lc.LendingId = lendings.Lendings.Id
WHERE personid = 561
ORDER BY deliverydate DESC
However, you have not shown the PersonLendings table, have you?
Try this one -
SELECT ls.status
, p.Productname
, p.Serial_number
, d.AMOUNT
, l.DeliveryDate
, l.Id AS LendingId
, p.Id AS ProductId
, pl.cnt
FROM dbo.Lendings l
LEFT JOIN (
SELECT pl.LendingId, cnt = COUNT(pl.LendingComments)
FROM dbo.PersonLendings pl
GROUP BY pl.LendingId
) pl ON pl.LendingId = l.LendingId
LEFT JOIN dbo.Products p ON l.ProductId = p.Id
LEFT JOIN dbo.LendingStatus ls ON l.StatusId = ls.Id
LEFT JOIN dbo.Deposits d ON l.DepositId = d.Id
WHERE PersonID = 561
ORDER BY l.DeliveryDate DESC