简体   繁体   中英

select based on calculated value, optimization

i need to select among other fields the age of a customer at the time he/she bought some product of a specific brand etc, WHERE the customer was for example between 30 and 50 years old.i wrote this query (getAge just uses DATEDIFF to return the age in years)

SELECT DISTINCT customers.FirstName, customers.LastName, 
             products.ProductName,
             dbo.getAge(customers.BirthDate,sales.Datekey)
              AS Age_when_buying
FROM sales
 INNER JOIN dates ON sales.Datekey=dates.Datekey
 INNER JOIN customers ON sales.CustomerKey=customers.CustomerKey
 INNER JOIN products ON sales.ProductKey=products.ProductKey
 INNER JOIN stores ON sales.StoreKey=stores.StoreKey

WHERE stores.StoreName = 'DribleCom Europe Online Store' AND
products.BrandName = 'Proseware' AND
dbo.getAge(customers.BirthDate, sales.Datekey) >= 30 AND
dbo.getAge(customers.BirthDate, sales.Datekey) <=50

and it works but i calculate the age three times.I tried to assign age_when_buying to a variable but it didn't work.My next thought was to use cursor but i feel that there is a more simple way i am missing.The question is: which is the appropriate way to solve this or what are my options?

Assuming that you only have a limited number of filters you'd like to apply, you could use a Common Table Expression to restructure your query.

I personally find it easier to see all the joins and such in one place, while the filters are similarly grouped together at the bottom...

WITH CTE AS(
    select customers.FirstName
         , customers.LastName
         , dbo.getAge(customers.BirthDate,sales.Datekey) AS Age_when_buying
         , sales.StoreName
         , products.BrandName
         , products.ProductName
    from sales
         INNER JOIN customers on sales.CustomerKey=customers.CustomerKey
         INNER JOIN products ON sales.ProductKey = products.ProductKey
         INNER JOIN stores ON sales.StoreKey = stores.StoreKey
)
SELECT DISTINCT FirstName, LastName, ProductName, Age_when_buying
FROM CTE
WHERE StoreName = 'DribleCom Europe Online Store'
  AND BrandName = 'Proseware'
  AND Age_when_buying BETWEEN 30 AND 50

You should use Cross Apply.

SELECT DISTINCT customers.FirstName, customers.LastName, 
             products.ProductName,
             age.age AS Age_when_buying
FROM sales
 INNER JOIN dates ON sales.Datekey=dates.Datekey
 INNER JOIN customers ON sales.CustomerKey=customers.CustomerKey
 INNER JOIN products ON sales.ProductKey=products.ProductKey
 INNER JOIN stores ON sales.StoreKey=stores.StoreKey
CROSS APPLY
(select dbo.getAge(customers.BirthDate, sales.Datekey) as age) age
WHERE stores.StoreName = 'DribleCom Europe Online Store' AND
products.BrandName = 'Proseware' AND
age.age >= 30 AND
age.age <=50

You could use a WITH clause :

WITH Customers_Info (CustomerFirstName, CustomerLastName, CustomerKey, AgeWhenBuying)
AS
(
    SELECT customers.FirtName,
           customers.LastName,
           CustomerKey
           dbo.getAge(customers.BirthDate, sales.DateKey) As AgeWhenBuying
    FROM customers
    JOIN sale USING(CustomerKey)
)
SELECT FirstName,
       LastName,
       products.ProductName,
       Customers_Info.AgeWhenBuying
    FROM Customers_Info
    JOIN sale USING(CustomerKey)
    JOIN products USING(ProductKey)
    JOIN stores USING(StoreKey)
    WHERE stores.StoreName = 'DribleCom Europe Online Store'
      AND products.BrandName = 'Proseware'
      AND Customers_Info.AgeWhenBuying >= 30
      AND Customers_Info.AgeWhenBuying <= 50;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM