I am trying to implement a pivoted table in sql but it is not working. What I currently have is the following:
WITH Pivoted
AS
(
select vg.ParentProductCategoryName, c.CompanyName, sd.LineTotal
FROM SalesLT.Product p join SalesLT.vGetAllCategories vg on p.ProductCategoryID = vg.ProductCategoryID
Join SalesLT.SalesOrderDetail sd on p.ProductID = sd.ProductID
JOIN SalesLT.SalesOrderHeader as soh ON sd.SalesOrderID = soh.SalesOrderID
JOIN SalesLT.Customer AS c ON soh.CustomerID = c.CustomerID
pivot(Sum([LineTotal]) for [ParentProductCategoryName] in (Accessories, Bikes, Clothing, Components)) AS sales
)
select * from Pivoted p;
;
I get the error:
multi part "Column name" Identifier could not be bounded.
If I removed the column names in the select part and used * instead, I get:
The column 'ProductCategoryID' was specified multiple times for...
What I want is to have a view of the total Revenue (as specified by the sum of the lineTotal in the SalesOrderDetail Table) per each ParentProductCategoryName (in vGetAllCategories) stated (pivoted as columns) with respect to each CompanyName (in Customer). How to better achieve this? Thanks.
Not sure why you'd need a CTE for this.. but put your JOINS in a derived table and pivot that derived table instead.
SELECT *
FROM (SELECT vg.ParentProductCategoryName,
c.CompanyName,
sd.LineTotal
FROM SalesLT.Product p
JOIN SalesLT.vGetAllCategories vg ON p.ProductCategoryID = vg.ProductCategoryID
JOIN SalesLT.SalesOrderDetail sd ON p.ProductID = sd.ProductID
JOIN SalesLT.SalesOrderHeader AS soh ON sd.SalesOrderID = soh.SalesOrderID
JOIN SalesLT.Customer AS c ON soh.CustomerID = c.CustomerID
) t
PIVOT( SUM([LineTotal])
FOR [ParentProductCategoryName] IN (Accessories,Bikes,Clothing,Components) ) AS sales
or you could just use the SUM aggregate with CASE
SELECT c.CompanyName,
Accessories = SUM(CASE WHEN vg.ParentProductCategoryName = 'Accessories' THEN sd.LineTotal END),
Bikes = SUM(CASE WHEN vg.ParentProductCategoryName = 'Bikes' THEN sd.LineTotal END),
Clothing = SUM(CASE WHEN vg.ParentProductCategoryName = 'Clothing' THEN sd.LineTotal END),
Components = SUM(CASE WHEN vg.ParentProductCategoryName = 'Components' THEN sd.LineTotal END)
FROM SalesLT.Product p
JOIN SalesLT.vGetAllCategories vg ON p.ProductCategoryID = vg.ProductCategoryID
JOIN SalesLT.SalesOrderDetail sd ON p.ProductID = sd.ProductID
JOIN SalesLT.SalesOrderHeader AS soh ON sd.SalesOrderID = soh.SalesOrderID
JOIN SalesLT.Customer AS c ON soh.CustomerID = c.CustomerID
GROUP BY c.CompanyName
Related
I'm trying to get total sum base on total_due column for the customerid where year = some year. but it does not sum correctly for example:
USE AdventureWorks2014
go
SELECT
soh.CustomerID
,CONCAT(p.FirstName, ' ', p.LastName) AS [Full Name]
,cr.Name AS Country
,SUM(soh.TotalDue) AS Total_Sales
,YEAR(soh.OrderDate) AS Order_Year
FROM
Sales.SalesOrderHeader soh
JOIN
Sales.SalesTerritory st ON soh.TerritoryID = st.TerritoryID
JOIN
Person.CountryRegion cr ON st.CountryRegionCode = cr.CountryRegionCode
JOIN
Sales.Customer c ON soh.CustomerID = c.CustomerID
JOIN
Person.Person p ON c.PersonID = p.BusinessEntityID
WHERE
YEAR(soh.OrderDate) = 2011
AND soh.CustomerID = 30100
GROUP BY
soh.CustomerID
,p.FirstName
,p.LastName
,cr.Name
,soh.OrderDate
I'm trying to do something like this but got an error and can't get how to do the last WHERE in the right way?
SELECT *
FROM
(
SELECT s.SupplierID, s.CompanyName,
(SELECT COUNT(*)
FROM dbo.Orders o
LEFT JOIN dbo.[Order Details] od ON o.OrderID = od.OrderID
LEFT JOIN dbo.Products p ON od.ProductID = p.ProductID
WHERE SupplierID = s.SupplierID) AS N'Number of orders'
FROM dbo.Suppliers s
)
WHERE 'Number of orders' > 150;
This is pretty sparse on details here but I think you are just trying to find and suppliers who have more than 150 orders. You can use a query with basic aggregation here and make this a lot simpler.
SELECT s.SupplierID
, s.CompanyName
, COUNT(*)
FROM dbo.Orders o
JOIN dbo.[Order Details] od ON o.OrderID = od.OrderID
JOIN dbo.Products p ON od.ProductID = p.ProductID
join dbo.Suppliers s on s.SupplierID = p.SupplierID
group by s.SupplierID
, s.CompanyName
having count(*) > 150
You need to name the main expression with an alias.
For example, this works:
SELECT * FROM (SELECT 1 AS One) O WHERE One = 1
But this does not:
SELECT * FROM (SELECT 1 AS One) WHERE One = 1
Try this:
SELECT *
FROM
(
SELECT s.SupplierID, s.CompanyName,
(SELECT COUNT(*)
FROM dbo.Orders o
LEFT JOIN dbo.[Order Details] od ON o.OrderID = od.OrderID
LEFT JOIN dbo.Products p ON od.ProductID = p.ProductID
WHERE SupplierID = s.SupplierID) AS N'Number of orders'
FROM dbo.Suppliers s
) T
WHERE 'Number of orders' > 150;
Also, as Uueerdo puointed out, it would be better to use [Square Brackets] instead of 'Single Quotes' to name and refer to the column [Number of orders]. I believe there is a setting that determines if single quotes are allowed, but square brackets are the standard.
Actual Data
using these queries:
Select P.productid, Sum(pr.quantity) AS PQuantity from Products p
join purchases pr on pr.Productid = p.productid
group by p.productid;
Select P.productid, Sum(s.quantity) AS SQuantity from Products p
join Sales s on s.Productid = p.productid
group by p.productid;
This is the content in my tables (Purchases and Sales), the one on the right is the Sum with group by:
Problem:
select p.productid, sum(pr.quantity) as PQuantity, sum(s.quantity) as SQuantity
from products p
join sales s on p.productid = s.productid
join purchases pr on p.productid = pr.productid
group by p.productid
But the problem arises when I join three tables:
products
purchases
sales
You'll have to sum the figures before joining, something like this:
select isnull(P.productid, S.productid), P.PQuantity, S.SQuantity
from
(
Select P.productid, Sum(pr.quantity) AS PQuantity from Products p
join purchases pr on pr.Productid = p.productid
group by p.productid
) P
full outer join
(
Select P.productid, Sum(s.quantity) AS SQuantity from Products p
join Sales s on s.Productid = p.productid
group by p.productid
) S on S.productid = P.productid
I have an eCommerce website where I am getting lot of fraud orders.. I'd like to pull out those Order_No.
Here is my query
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
Here's the criteria I want to follow:
If the customer_id repeats more than once in 6hrs
If the IPAddress repeats more than once in 6hrs
If the Lastname is NOT found in Nameoncard
Can someone help please?
can you try this
WITH Tmp (Order_No, Customer_id, DateOrdered, IPAddress, FirstName, LastName, NameOnCard)
AS
(
SELECT Ord.Order_No, Ord.Customer_Id, Ord.DateOrdered, Ord.IPAddress,
Cust.FirstName, Cust.LastName, CustData.NameOnCard
FROM Order_No Ord
INNER JOIN Customers Cust
ON
Cust.Customer_Id = Ord.Customer_Id
INNER JOIN
CardData CustData
ON CustData.Id = Ord.Card_Id
)
SELECT DISTINCT a.*
FROM Tmp a
INNER JOIN Tmp b
ON a.Order_No <> b.Order_No
AND a.Customer_Id = b.Customer_Id
WHERE DATEDIFF(hour, a.DateOrdered, b.DateOrdered) >= 6
UNION
SELECT DISTINCT c.*
FROM Tmp c
INNER JOIN Tmp d
ON c.Order_No <> d.Order_No
AND c.IPAddress = d.IPAddress
WHERE DATEDIFF(hour, c.DateOrdered, d.DateOrdered) >= 6
UNION
SELECT DISTINCT e.*
FROM Tmp e
WHERE ISNULL(e.NameOnCard,'') = ''
here is the query:
select * from
(
select b.order_no,b.dateordered,a.customer_id, C.FirstName, C.LastName, cd.nameoncard from order_no as a
left join order_no as b on a.customer_id=b.customer_id
inner join carddata as cd on b.customer_id=cd.customer_id
INNER JOIN Customers C ON b.customer_id = C.customer_id
where a.order_no < b.order_no
and datediff(hour,a.dateordered,b.dateordered) between 0 and 6
union
select b.order_no,b.dateordered,a.customer_id, C.FirstName, C.LastName, cd.nameoncard from order_no as a
left join order_no as b on a.IPAddress=b.IPAddress
inner join carddata as cd on b.customer_id=cd.customer_id
INNER JOIN Customers C ON b.customer_id = C.customer_id
where a.order_no < b.order_no
and datediff(hour,a.dateordered,b.dateordered) between 0 and 6
union
select a.order_no,a.dateordered,a.customer_id, C.FirstName, C.LastName, cd.nameoncard from order_no as a
inner join carddata as cd on a.customer_id=cd.customer_id
INNER JOIN Customers C ON a.customer_id = C.customer_id
where charindex(C.LastName,cd.nameoncard) = 0
) as abc
I'm trying to use a calculated column in a where clause.
I've trying everything from CROSS APPLY, to sub-query select but it does not give me the anything near what I need.
My query so far:
SELECT p.Code, c.AccountNumber, Sales = (SUM(p.UnitPrice) * SUM(od.QtyShipped)) FROM [dbo].Customer c
LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id
LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
WHERE Sales > 100
GROUP BY p.Code, c.AccountNumber, Sales
This does not work, as 'Sales' is an invalid column
Using Derived Columns in a predicate
You'll need to wrap the inner query in a derived table or CTE in order to be able to use derived columns in the WHERE clause (Also, note SUM() is specified just once, using the results of the multiplication):
SELECT x.Code, x.AccountNumber, x.Sales
FROM
(
SELECT p.Code, c.AccountNumber, SUM(p.UnitPrice *od.QtyShipped) AS Sales
FROM [dbo].Customer c
LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id
LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
GROUP BY p.Code, c.AccountNumber
) AS x
WHERE x.Sales > 100;
Repeating the Derived Column in a HAVING clause
As per #Jonny's comment, the other way is not to DRY up the calculated column, but to instead repeat the calculation. Use HAVING instead of WHERE after a GROUP BY has been applied.
SELECT p.Code, c.AccountNumber, SUM(p.UnitPrice *od.QtyShipped) AS Sales
FROM [dbo].Customer c
LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id
LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
GROUP BY p.Code, c.AccountNumber
HAVING SUM(p.UnitPrice * od.QtyShipped) > 100;
In either case, as per comments below, note that the calculated expression is SUM(p.UnitPrice * od.QtyShipped) and not SUM(p.UnitPrice) * SUM(od.QtyShipped).
You can use the common table expression for this
;WITH CTE AS
(
SELECT p.Code, c.AccountNumber, Sales = (SUM(p.UnitPrice) * SUM(od.QtyShipped)) FROM [dbo].Customer c
LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id
LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
GROUP BY p.Code, c.AccountNumber, Sale
)
SELECT *
FROM CTE WHERE CTE.Sales>100
If it's a calculated column you can use "HAVING".
SELECT p.Code, c.AccountNumber, Sales = (SUM(p.UnitPrice) * SUM(od.QtyShipped)) FROM [dbo].Customer c
LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id
LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
GROUP BY p.Code, c.AccountNumber, Sales
HAVING SALES > 100;