开发者

Help with complex sql query

To make story short, i'm building self-learning banner management system. Users will be able to insert these banners to their site when banners will be 开发者_如何学Pythonshown based on sales/impressions ratio.

I have 4 tables

Banners 
bannerID      int
bannerImage   varchar....

SmartBanners
smartBannerID      int
smartBannerArrayID int
bannerID           int
impressionsCount   int
visibility         tinyint (percents)

SmartBannerArrays 
smartBannerArrayID int
userID             int

Statistics
bannerID          int
saleAmountPerDay  decimal...

Each night i need to generate new "visibility" for each SmartBanner based on whole SmartBannerArray that same user has. So i need to get sum of impressions and sales for each bannerID in SmartBannerArray.

All comes to my mind is to use double cursor, one will loop thought SmartBannerArrays get needed values for sum of impressions and sales and then inner loop which will access each SmartBanner and change it's "visibility" percentage based on (sales/impressions)/(sumOfSales/sumOfImpressions)*100

Hope you get the picture...

Is there any other way to design better tables or not to use double cursor to avoid server overload ?

MORE INFO Each sale written to Statistics table , field is updated because i need daily sum per banner rather each sale. User create BannerArray, choose products he would like to promote. Each product he chose is written to Banners table with proper image and other info. SmartBanners table stores bannerID so as Statistics table, while BannerArray table assigns this group of banners to certain user.


The picture is not so clear. Why cursors at all, what is wrong with this?

SELECT SUM(saleAmountPerDay)/SUM(impressionsCount) 
FROM SmartBanners sb        INNER JOIN
     SmartBannerArrays sba  ON sb.smartBannerArrayID = sba.smartBannerArrayID INNER JOIN
     Statistics ss          ON sb.bannerID = ss.bannerID 
GROUP BY smartBannerArrayID

Which can be used then as subquery to calculate 'visibility' directly.

EDIT2: Illustrating the principle (SQL not optimized), why not:

UPDATE SmartBanners
SET visibility = 
    ROUND( 100. * 
    (
    SELECT SUM(saleAmountPerDay)/SUM(impressionsCount) 
    FROM SmartBanners sb        INNER JOIN
         Statistics ss          ON sb.bannerID = ss.bannerID 
    WHERE sb.smartBannerID = SmartBAnners.smartBannerID
    ) /
    (
    SELECT SUM(saleAmountPerDay)/SUM(impressionsCount) 
    FROM SmartBanners sb        INNER JOIN
         SmartBannerArrays sba  ON sb.smartBannerArrayID = sba.smartBannerArrayID INNER JOIN
         Statistics ss          ON sb.bannerID = ss.bannerID 
    WHERE sb.smartBannerArrayID = SmartBAnners.smartBannerArrayID
    ) )

if you want to avoid the cursors.

Also, not to forget - if this data is not going to be updated often (what's the frequency of the update of the statistics table? I imagine you update it periodically from some logs) AND if you are looking to maximize read performance you might consider triggers.

EDIT3: In MS SQL you can also use OVER clause, just a short example

SELECT 
       SUM(saleAmountPerDay) OVER(PARTITION BY BannerID) AS 'TotalSalesByBanner',
       SUM(impressionsCount) OVER(PARTITION BY BannerID) AS 'TotalImpressionsByBanner',
       SUM(saleAmountPerDay) OVER(PARTITION BY smartBannerArrayID) AS 'TotalSalesByArray',
       SUM(impressionsCount) OVER(PARTITION BY smartBannerArrayID) AS 'TotalImpressionsByBanner'    
FROM   
       SmartBanners sb INNER JOIN
       Statistics ss   ON sb.bannerID = ss.bannerID
GROUP BY
       SmartBannerID

This SQL is not tested.


In many cases the use of cursors can be avoided by using derived tables. An introduction to the use of them can be found here.


I think you're asking the right question, which is: is there any way to design better tables? My advice: worry less about server overload, which is no big deal if you're running queries only once a day. Worry more about a comprehensible structure.

What about this:

 Banners
 -------
 BannerID
 UserID
 BannerImage
 BannerVisibility  // A denormalized value that you calculate once a day

 Banner_Sales
 ------------
 BannerID
 Date
 SalesAmt

 Banner_Impressions
 ------------------
 BannerID
 Date
 Impressions

You don't need a SmartBannerArray - or cursors - to sum the sales and impressions. Just use a SQL sum:

 SELECT SUM(bs.SalesAmt) AS SALES, SUM(bi.Impressions) AS IMPRESSIONS, 
   SUM(bs.SalesAmt) / SUM(bi.Impressions) AS RATIO
 FROM Banners b
 JOIN Banner_Sales bs
 JOIN Banner_Impressions bi
 WHERE b.UserID = ? // For total sales / impressions for a user

Substitute

WHERE b.BannerID = ? // For total sales / impressions for a banner

Good luck!

P.S. tinyint's are for integers, not percentages. You'll need a double. :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜