設為首頁收藏本站Access中國

Office中國論壇/Access中國論壇

 找回密碼
 注冊

QQ登錄

只需一步,快速開始

返回列表 發(fā)新帖
查看: 3086|回復: 1
打印 上一主題 下一主題

淺談數(shù)據(jù)庫開發(fā)(五)——從寬表到存儲過程(之四)

[復制鏈接]
跳轉(zhuǎn)到指定樓層
1#
發(fā)表于 2019-11-16 16:00:38 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
我們在前面已經(jīng)寫了一個存儲過程,但很明顯,我們的update語句挺多的,9個字段,就需要寫個update語句,假若某天老板要看一個月的,豈不是要定義31個變量,然后更新31次?我們也沒有更好的辦法呢?答案是:有。
——寫循環(huán)語句。

我們知道,這些update語句的格式都是相似的。理論上,我們可以把update那部分剝離出來,作為一個子過程來調(diào)用,寫循環(huán)即可。但現(xiàn)實是,更新的字段不一致,這將涉及到SQL語句字符串的拼接。所以暫時我們不考慮用子過程的方式來完成這個任務,而是就地修改存儲過程。
對于經(jīng)驗不太豐富的新手,在拼接SQL字符串時,個人建議先使用print的方法來檢查語法是否正確。正確后,再進行正式的執(zhí)行。
我們先來看看這些語句是結(jié)構(gòu):
  1. update log_result set log_result.day0=1 from (select 用戶ID from log_info where 登錄時間=@day0) b
  2. where log_result.userID=b.用戶ID
復制代碼
從語句中,我們知道a表中,字段day0可以作為一個循環(huán)變量(day0~day8),而b表中的@day0同樣也可以作為一個循環(huán)變量(@day0~@day8),而其它的則無需更改。此外,day0和@day0原則上應該是一一對應的。因此,接下來,我們可以聲明一個SQL字符串用于存放語句,聲明一個@i用于循環(huán)賦值,并且初始化,右擊,修改存儲過程:
  1. USE [test]
  2. GO
  3. /****** Object:  StoredProcedure [dbo].[sp_user_count]    Script Date: 2019-11-16 14:54:37 ******/
  4. SET ANSI_NULLS ON
  5. GO
  6. SET QUOTED_IDENTIFIER ON
  7. GO
  8. -- =============================================
  9. -- Author:                Roych
  10. -- Create date: 2019-11-16
  11. -- Description:        用于完成用戶留存率數(shù)據(jù)源的計算
  12. -- =============================================
  13. ALTER PROCEDURE [dbo].[sp_user_count] @min_date date=null
  14. AS
  15. BEGIN
  16.         SET NOCOUNT ON;
  17.         --定義9個日期變量
  18.         declare @day0 date,@day1 date,@day2 date,@day3 date,@day4 date
  19.                         ,@day5 date,@day6 date,@day7 date,@day8 date
  20.         declare @SQL varchar(500)
  21.         declare @i int
  22.         /*
  23.         另一種變量聲明的寫法:
  24.         declare @day0 date
  25.         declare @day1 date
  26.         ……………………
  27.         declare @dayn date
  28.         */
  29.         --如果使用默認值(即無輸入),則預設為系統(tǒng)日期前8天。
  30.         if @min_date is null
  31.                 set @min_date=dateadd(day,-8,GETDATE())
  32.         set @day0=@min_date
  33.         set @day1=DATEADD(day,1,@min_date)
  34.         set @day2=DATEADD(day,2,@min_date)
  35.         set @day3=DATEADD(day,3,@min_date)
  36.         set @day4=DATEADD(day,4,@min_date)
  37.         set @day5=DATEADD(day,5,@min_date)
  38.         set @day6=DATEADD(day,6,@min_date)
  39.         set @day7=DATEADD(day,7,@min_date)
  40.         set @day8=DATEADD(day,8,@min_date)

  41.         --初始化
  42.         set @SQL=''
  43.         set @i=0
  44. /*
  45. --清空舊數(shù)據(jù)
  46. truncate table log_result
  47. --插入新數(shù)據(jù)(這里只插入day0~day8的數(shù)據(jù),并初始化登錄天數(shù)為0)
  48. insert into log_result
  49. select 用戶ID,min(登錄時間),0,0,0,0,0,0,0,0,0
  50. from log_info where 登錄時間 between @day0 and @day8
  51. group by 用戶ID
  52. */
  53. set @SQL='update log_result set log_result.day'+CONVERT(varchar(2),@i)
  54.                  +'=1 from (select 用戶ID from log_info where 登錄時間='+convert(varchar(10),@day0,120)
  55.                  +') b where log_result.userID=b.用戶ID'
  56. print(@SQL)

  57. /*
  58. --更新字段day0~day8
  59. update log_result set log_result.day0=1 from (select 用戶ID from log_info where 登錄時間=@day0) b
  60. where log_result.userID=b.用戶ID

  61. update log_result set log_result.day1=1 from (select 用戶ID from log_info where 登錄時間=@day1) b
  62. where log_result.userID=b.用戶ID

  63. update log_result set log_result.day2=1 from (select 用戶ID from log_info where 登錄時間=@day2) b
  64. where log_result.userID=b.用戶ID

  65. update log_result set log_result.day3=1 from (select 用戶ID from log_info where 登錄時間=@day3) b
  66. where log_result.userID=b.用戶ID

  67. update log_result set log_result.day4=1 from (select 用戶ID from log_info where 登錄時間=@day4) b
  68. where log_result.userID=b.用戶ID

  69. update log_result set log_result.day5=1 from (select 用戶ID from log_info where 登錄時間=@day5) b
  70. where log_result.userID=b.用戶ID

  71. update log_result set log_result.day6=1 from (select 用戶ID from log_info where 登錄時間=@day6) b
  72. where log_result.userID=b.用戶ID

  73. update log_result set log_result.day7=1 from (select 用戶ID from log_info where 登錄時間=@day7) b
  74. where log_result.userID=b.用戶ID

  75. update log_result set log_result.day8=1 from (select 用戶ID from log_info where 登錄時間=@day8) b
  76. where log_result.userID=b.用戶ID
  77. */
  78. END
復制代碼
變量@SQL的具體長度,可以根據(jù)實際設置。我這里稍稍設置偏大一些。由于我們的目的是檢查SQL語句,因此,可以先把前面的各種操作注釋掉(truncate,insert和update等)。完成后點工具欄上的三角按鈕“執(zhí)行”。然后同樣按前面提到的方法執(zhí)行,結(jié)果生成了這樣的語句:

我們留意到,這個并不是日期格式(日期格式是需要加上單引號的)。因此需要修改前面的語句為:
  1. set @SQL='update log_result set log_result.day'+CONVERT(varchar(2),@i)
  2.                  +'=1 from (select 用戶ID from log_info where 登錄時間='''+convert(varchar(10),@day0,120)
  3.                  +''') b where log_result.userID=b.用戶ID'
復制代碼
這是SQL語句中的用法。使用單引號時,是加一對的,請自行對比。這時候同樣再執(zhí)行一次,得到正確的結(jié)果。這里就不再截圖了。
可能有人會問,如果本身就是字符串,還里面還有單引號怎么辦?答案很簡單,再加一對!@種情況也是有的。在鏈接服務器上同步數(shù)據(jù)時,其中一個參數(shù)就是SQL字符串,以單引號括起來引用。如果需要傳日期值給這個字符串,那么就得看加上兩對(即4個單引號)了。詳情請留意拙作:
巧用鏈接服務器同步數(shù)據(jù)
這里暫時不討論這個問題。確實不懂的版友回復本帖后,我再貼答案。

至此,我們寫了一個簡單的SQL語句拼接,接下來,我們將它修改為while循環(huán),當然這時候的@day0就不能直接使用了,而是應該改為dateadd了:
  1. while @i<9
  2. begin
  3. set @SQL='update log_result set log_result.day'+CONVERT(varchar(2),@i)
  4.                  +' =1 from (select 用戶ID from log_info where 登錄時間='''+convert(varchar(10),DATEADD(day,@i,@min_date),120)
  5.                  +''') b where log_result.userID=b.用戶ID'
  6.         print(@SQL)
  7. set @i=@i+1
  8. end
復制代碼
同樣地,我們執(zhí)行修改后,再來執(zhí)行一下存儲過程,于是得到以下結(jié)果:
看起來,確實是我們想要的結(jié)果。
既然語句沒問題了,那么,我們就可以把print語句改成exec了。刪除不必要的變量和腳本之后,得到以下存儲過程:
  1. USE [test]
  2. GO
  3. /****** Object:  StoredProcedure [dbo].[sp_user_count]    Script Date: 2019-11-16 14:54:37 ******/
  4. SET ANSI_NULLS ON
  5. GO
  6. SET QUOTED_IDENTIFIER ON
  7. GO
  8. -- =============================================
  9. -- Author:                Roych
  10. -- Create date: 2019-11-16
  11. -- Description:        用于完成用戶留存率數(shù)據(jù)源的計算
  12. -- =============================================
  13. ALTER PROCEDURE [dbo].[sp_user_count] @min_date date=null
  14. AS
  15. BEGIN
  16.         SET NOCOUNT ON;
  17.         --定義9個日期變量
  18.         declare @SQL varchar(500)
  19.         declare @i int
  20.         --如果使用默認值(即無輸入),則預設為系統(tǒng)日期前8天。
  21.         if @min_date is null
  22.                 set @min_date=dateadd(day,-8,GETDATE())
  23.         --初始化
  24.         set @SQL=''
  25.         set @i=0
  26. --清空舊數(shù)據(jù)
  27. truncate table log_result
  28. --插入新數(shù)據(jù)(這里只插入day0~day8的數(shù)據(jù),并初始化登錄天數(shù)為0)
  29. insert into log_result
  30. select 用戶ID,min(登錄時間),0,0,0,0,0,0,0,0,0
  31. from log_info where 登錄時間 between @min_date and dateadd(day,8,@min_date)
  32. group by 用戶ID
  33. --執(zhí)行day0~day8的更新查詢
  34. while @i<9
  35. begin
  36. set @SQL='update log_result set log_result.day'+CONVERT(varchar(2),@i)
  37.                  +' from (select 用戶ID from log_info where 登錄時間='''+convert(varchar(10),DATEADD(day,@i,@min_date),120)
  38.                  +''') b where log_result.userID=b.用戶ID'
  39.         exec(@SQL)
  40. set @i=@i+1
  41. end

  42. END
復制代碼
這樣,看起來是不是清爽多了?驗證一下,看看結(jié)果如何:

看起來沒什么問題。那么結(jié)果表就可以用這樣寫了:

稍稍對比下,看上去應該對得上。

至此,一個存儲過程就開發(fā)完成了。學會了么?附上附件,供參考。

有什么問題,歡迎跟帖。

本帖子中包含更多資源

您需要 登錄 才可以下載或查看,沒有帳號?注冊

x
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享分享 分享淘帖 訂閱訂閱
2#
發(fā)表于 2021-10-11 10:20:35 | 只看該作者

學習
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則

QQ|站長郵箱|小黑屋|手機版|Office中國/Access中國 ( 粵ICP備10043721號-1 )  

GMT+8, 2025-7-13 02:37 , Processed in 0.080727 second(s), 26 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回復 返回頂部 返回列表