I need to find Customer transaction that didn't occur while a customer was considered a "premium customer". In this example, the sql retrieves row #2. How do I avoid retrieving any records since this customer was a premium on purchase date as referenced in row 1.
SELECT
FROM TblPremCus c INNER JOIN tblTrans t ON c.CustomerID = t.CustomerID
WHERE t.PurchaseDate NOT BETWEEN c.StartDate AND c.EndDate
Data:
**tblPremCus**
ROWID CustomerID StartDate EndDate
1 ABC123 1/1/2016 6/16/2016
2 ABC123 9/3/2016 12/21/9999
**tblTrans**
TransID CustomerID PurchaseDate
T1 ABC123 6/1/16
Expected Result: NONE
My understanding is that you want to get all transactions where the transaction date does not fall in between the dates that customer is 'premium'.
In a case like this, though, it is easier to find transactions that DO fall into one of those ranges, and is a simple change from what you started with:
SELECT * FROM TblPremCus c
INNER JOIN tblTrans t ON c.CustomerID = t.CustomerID
WHERE t.PurchaseDate BETWEEN c.StartDate AND c.EndDate
We've seen that using NOT BETWEEN is not good enough to weed out unwanted transactions, but now that we know which transactions we DON'T want, we can find the ones we DO: simply, any ones not returned by the above query.
SELECT * from TblTrans
WHERE TransID NOT IN (SELECT TransID FROM TblPremCus c
INNER JOIN tblTrans t ON c.CustomerID = t.CustomerID
WHERE t.PurchaseDate BETWEEN c.StartDate AND c.EndDate)
Related
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.
I am getting my face kicked in....
I have a total of 4 tables
1. Business (BusinessID, CustomerID, BusName, Territory)
2. Customer (CustomerID, Name)
3. Sales (BusinessID, CustomerID, Territory, Jan, Feb, Mar, Apr, May, Jun)
4. Performance (this is the table I want the info in)
I've already created the table to have the following columns, BusinessID, CustomerID, BusName, Name, Territory, Jan,Feb,Mar,Apr,May,Jun
Every time I try to insert its not properly joining and I am getting a bunch of errors "multi-part identifier could not be bound"
insert into Performance (BusinessID, CustomerID, BusName, Name, Territory, January2018, February2018, March2018, April2018, May2018, June2018)
select Business.BusinessID, Customer.CustomerID, Business.BusName, Customer.Name, Sales.Territory, Sales.January2018, Sales.February2018, Sales.March2018, Sales.April2018, Sales.May2018, Sales.June2018
from Business A
inner join Customer B ON a.CustomerID = b.CustomerID
inner join Sales C ON b.CustomerID = c.CustomerID;
Due to this error I had to do 3 seperate insert into and that caused a bunch of nulls....
face palm is happening and could use some advice.
Image: enter image description here
Thanks,
VeryNew2SQL
You have used table ALIASES, so you have to use those aliases in you SELECT
A for Business, B for Customer and C for Sales.
Read about ALIASES here.
select A.BusinessID, B.CustomerID, A.BusName, B.Name, C.Territory, C.January2018, C.February2018, C.March2018, C.April2018, C.May2018, C.June2018
from Business A
inner join Customer B ON a.CustomerID = b.CustomerID
inner join Sales C ON b.CustomerID = c.CustomerID;
When you create a table alias in your FROM and JOIN clauses, you need to refer to the aliases in your SELECT statement and not the actual table names.
Alternatively, leave your SELECT statement as it is, and adjust your table names to remove the alias. You'll then need the join conditions to refer to your actual table names, rather than the alias. So for example;
select Business.BusinessID, Customer.CustomerID, Business.BusName, Customer.Name, Sales.Territory, Sales.January2018, Sales.February2018, Sales.March2018, Sales.April2018, Sales.May2018, Sales.June2018
from Business
inner join Customer ON Business.CustomerID = Customer.CustomerID
inner join Sales ON Customer.CustomerID = Sales.CustomerID;
Even just try running the SELECT statement above first to make sure you get the query correct before trying it in your insert.
I'm extremely new to SQL Sever and so I apologize if the question is worded strange. I am doing a homework assignment, and this is the question:
"A manager wants to know the email address, number or orders, and the total amount of purchases made by each customer. Create a summary query that returns these three items for each customer that has orders."
I have all of the data queried, the problem is when I pull data from each customer, it will show the quantity of items per order, and I need the items to be pooled together into one column. This is my query thus far (again, total noob, please excuse any poor syntax, etc.)
SELECT EmailAddress,
ItemPrice - DiscountAmount * Quantity AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers
JOIN Orders ON Customers.CustomerID = Orders.CustomerID
JOIN OrderItems ON Orders.OrderID = OrderItems.OrderID
GROUP BY Orders.CustomerID,
OrderItems.ItemPrice, OrderItems.DiscountAmount,
OrderItems.Quantity,
Customers.EmailAddress;
The following is a small bit of the result set that I get:
Email Address OrderTotal OrderQty
allan.sherwood#yahoo.com 253.15 2
allan.sherwood#yahoo.com 839.30 2
allan.sherwood#yahoo.com 1208.16 2
barryz#gmail.com 303.79 4
christineb#solarone.com 479.60 2
david.goldstein#hotmail.com 299.00 2
david.goldstein#hotmail.com 489.30 1
david.goldstein#hotmail.com 479.60 1
So as you can see, I have several orders I need to smoosh together into one single row per e-mail, I have looked and looked for an answer but the only thing I can find is how to find duplicates and ignore them, not combine their data. Any help is extremely appreciate, thanks so much for taking the time to read this :) If my question doesn't make sense please let me know so I can clear up any bad wording I may have used!
Just do GROUP BY CustomerID, EmailAddress:
SELECT
c.EmailAddress,
SUM((i.ItemPrice - i.DiscountAmount) * Quantity) AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers c
INNER JOIN Orders o
ON c.CustomerID = o.CustomerID
INNER JOIN OrderItems i
ON o.OrderID = i.OrderID
GROUP BY
c.CustomerID, c.EmailAddress
Additional note: Use aliases for your tables
You need to change your formula and remove columns that you dont want to group by from select query..
for example your query should be something like this
SELECT EmailAddress,
--do your aggregation here
blah AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers
JOIN Orders ON Customers.CustomerID = Orders.CustomerID
JOIN OrderItems ON Orders.OrderID = OrderItems.OrderID
GROUP BY Orders.CustomerID,
Customers.EmailAddress;
I've never worked with a non-normalized database before, so I'll try and explain my problem as best I can. So I have two tables:
The customers table holds all the customers information, and the orders table holds all the orders that they have placed. I haven't listed all the fields in the tables, just the ones that I need. The customer number in both tables is not the primary key, but I'm inner joining on them anyway. So the problem I'm having is that I don't know how to make a query that:
Selects all the customers with their first name, last name, and email, and also show the most recent orderdate, most recent total, and most recent ordertype. I know that I have to use a max() aggregate for the date, but that's as far as I got. Please help a noob out.
You can try:
SELECT FirstName,
LastName,
Email,
OrderDate,
OrderTotal,
OrderType
FROM Customers AS C
INNER JOIN Order AS O
ON O.CustomerNumber = C.CustomerNumber AND
O.OrderDate = (
SELECT MAX (O1.OrderDate)
FROM Order AS O1
WHERE O1.CustomerNumber = C.CustomerNumber)
)
assuming that Orders.OrderDate is unique for each CustomerNumber, does this work for you? if a single CustomerNumber has more than one entry in Order for OrderDate, you'll get each of those rows.
select c.FirstName, c.LastName, c.Email, o.OrderDate, o.OrderTotal, o.OrderType
from Customers c
join
(select CusomterNumber, max(OrderDate) as MostRecentOrderDate
from Orders
group by CustomerNumber
) mro on mro.CustomerNumber=s.CustomerNumber
join Orders o on o.OrderDate=mro.MostRecentOrdeDate and
o.CustomerNumber=mro.CustomerNumber
Try this:
SELECT
Customers.*, Orders.*
FROM
Customers
JOIN
(SELECT
Customer_Number,
MAX(Order_Date) OrderDate
FROM
Orders
GROUP BY
Customer_Number
) as Ord ON Customers.Customer_Number = Ord.Customer_Number
JOIN Order ON Orders.Customer_Number = Ord.Customer_Number
If you are doing this with SQL Server use the query designer and basically all you want to do is do a join since you have two keys that are the same one in Customer Table ->Customer Join on Order->Customer alias the Customer table as C and Orders table as O
so for example
SELECT Customer.*, Orders.*
From Customer c, Orders O INNER JOIN O where C.Customer Number = O.Customer Number
This should be enough to get you started.. if you don't want all the fields then fully qualify the names for example
SELECT C.FirstName, C.LastName, O.OrderDate, O.OrderType FROM Customer C, Orders O
WHERE C.Customer NUmber = O.Customer Number //this is another way of doing a Join when working with the where Clause.
This is going to seem like a lame question for all experts in SQL server views but...
So I have a small set of data that my client needs for reporting purposes. I have to admit that although I did ask them their reporting requirements, it isn't till now that I see that my db could be better optimised.
One of the pieces of data they want is the time difference between two tasks that may have run:
select caseid, hy.createdate
from app_history hy
where hy.activityid in (303734, 303724)
This gives me two rows (after edit) per case-submission which then have to be measured; but a few wiggles:
Activity 303734 will always run, activity 303724 might run.
Each 303734 and 303724 combo match up. Conceiveably a case can have 1 un-matched 303734 with a matched pair afterwards on the 2nd submission. Matching these might be down to intuition. Not good.
There maybe more than one submission per caseid and if that is the case then both activities will run every subsequent time.
There is no way to write the submission number to this table.
The app_history table holds userid, caseid and activityid as foreign keys. The PK is the identity column ID.
Is there a better way to write the query?
AFter help from KM:
select
c.id, c.submissionno, hya.caseid, hya.createtime, hyb.caseid, hyb.createtime
,CASE
WHEN hyb.caseid IS NOT NULL THEN DATEDIFF(mi,hya.createtime,hyb.createtime)
ELSE NULL
END AS Difference
from app_case c
inner join app_history hya on c.id = hya.caseid
left outer join app_history hyb on c.id = hyb.caseid
where hya.activityid in (303734) and hyb.activityid in (303724) order by c.id asc
This nearly works.
I now have this issue:
460509|2|460509|15:15:39.000|460509|15:16:13.000|1
460509|2|460509|15:15:39.000|460509|15:18:13.000|3
460509|2|460509|15:17:52.000|460509|15:16:13.000|-1
460509|2|460509|15:17:52.000|460509|15:18:13.000|1
So I am now getting 1 row comparing each of the two for each of the four rows... mmm I think it is the best I can hope for. :(
USE LEFT JOIN
SELECT
a.caseid, a.createdate
,b.caseid, b.createdate
,CASE
WHEN b.caseid IS NOT NULL THEN DATEDIFF(mi,a.createdate,b.createdate)
ELSE NULL
END AS Difference
FROM app_history a
LEFT OUTER JOIN app_history b ON b.activityid=303724
WHERE a.activityid=303734
EDIT after a little more schema info...
SELECT
a.caseid, a.createdate
,b.caseid, b.createdate
,CASE
WHEN b.caseid IS NOT NULL THEN DATEDIFF(mi,a.createdate,b.createdate)
ELSE NULL
END AS Difference
FROM (SELECT MAX(ID) AS MaxID FROM app_history WHERE activityid=303734) aa
INNER JOIN app_history a ON aa.MaxID=a.ID
LEFT OUTER JOIN a(SELECT MAX(ID) AS MaxID FROM app_history WHERE activityid=303724) bb ON 1=1
LEFT OUTER JOIN app_history b ON bb.MaxID=b.ID
do something like this
select datediff(
day,
(select isnull(hy.createdate,0) from app_history hy where hy.activityid =303734),
(select isnull(hy.createdate,0) from app_history hy where hy.activityid =303724)
)