面試的時一道sql的解答

 


 

      在一家上市公司的一道sql題目,但是我沒弄出來,估計沒多少戲了,其實這道題在實踐中用過,無非就是一個行轉列,列轉行的問題,我怎麼那麼豬腦袋了,下面我是網絡上找到的比較全的分析,學習下人家的思路

1、    強大的group by

1  select stdname,
2  isnull(sum( case  stdsubject when  ' 化學 '  then Result end), 0 ) [化學],
3  isnull(sum( case  stdsubject when  ' 數學 '  then Result end), 0 ) [數學],
4  isnull(sum( case  stdsubject when  ' 物理 '  then Result end), 0 ) [物理],
5  isnull(sum( case  stdsubject when  ' 語文 '  then Result end), 0 ) [語文]
6  from #student
7  group by stdname

在這裏,group by與sum + case結合,可以將表1中的記錄(行)變成表2的字段(列)。Sum裏面如果沒有case,那麼出來的值,只能是全部科目的總和,用了case以後,就 是某科的成績;然後這裏用了好幾個sum,每個科目一個sum,於是表1中本來某人某科佔一條記錄的“行”就變成了表2裏某人一條記錄,每科做一個字段 了。


這種心思巧妙和對語法的熟練運用讓人擊節讚歎。


2、    利用select from (select from)的模式生成SQL語句

1  declare @sql varchar( 4000 )
2  set  @sql  =   ' select stdname ' 
3  select @sql  =  @sql  +   ' ,isnull(sum(case stdsubject when  ''' + stdsubject + '''  then Result end),0) [ ' + stdsubject + ' ] ' 
4  from (select distinct stdsubject from #student)  as  a
5  select @sql  =  @sql + '  from #student group by stdname ' 
6  print @sql
7  exec(@sql)


爲了自動寫上所有的科目,這裏先將科目信息提煉出來:

4  from (select distinct stdsubject from #student)  as  a

利用之拼接生成SQL語句。當然現實中,如果#student表很大,這種做法並不妥,應該都有一個專門的科目類別表的。


3、    在臨時庫中提煉出字段名。臨時表是真實存在的表,保存在[tempdb]中,可以利用object_id('tempdb.dbo.表名')的方式獲得字段信息。

============================================

附錄:

 

 

      縱覽各大社區、論壇,各大 ORM框架火得不行了,如NHibernate、LINQ to SQL、ADO.NET Entity framework等,還有最近市場上出版的一本叫《領域驅動設計與模式實戰》,裏面也凸顯了不少NHibernate在領域驅動設計中的作用與地位,也 算是第一本與NHibernate相關的書籍吧!不過就NHibernate而言還是沒有官方文檔介紹得詳細呵呵,園子裏Kiler 已 經把他翻譯成中文版的了,收益一大片僅僅是CET-4的人。不管你是用NHibernate也好,還是用LINQ to SQL也好,用profiler一跟蹤,執行的都是SQL語句,所以所SQL是根。特別是對於那些以數據爲中心的應用系統,在數據庫中實現複雜的存儲過 程,複雜的報表查詢,還是直接SQL來得痛快。當然 對 於那些在基於.NET的中間層應用中,它們實現面向對象的業務模型和商業邏輯的應用,NHibernate是最有用的。不管怎樣,NHibernate一 定可以幫助你消除或者包裝那些針對特定廠商的SQL代碼,並且幫你把結果集從表格式的表示形式轉換到一系列的對象去(官方文檔)。

      有點跑題了,不再囉嗦----直接晾出壓軸題。

壓軸題第一問

1.把表一轉換爲表二

表一:                                                                     

 

表二:

 

數據庫代碼如下:

  代碼
 1  DROP table #student
 2  CREATE TABLE #student (stdname nvarchar( 10 ),stdsubject nvarchar( 10 ),result  int )
 3  INSERT INTO #student VALUES ( ' 張三 ' , ' 語文 ' , 80 )
 4  INSERT INTO #student values ( ' 張三 ' , ' 數學 ' , 90 )
 5  INSERT INTO #student VALUES ( ' 張三 ' , ' 物理 ' , 85 )
 6  INSERT INTO #student VALUES ( ' 李四 ' , ' 語文 ' , 85 )
 7  INSERT INTO #student values ( ' 李四 ' , ' 數學 ' , 92 )
 8  INSERT INTO #student VALUES ( ' 李四 ' , ' 物理 ' , 82 )
 9  INSERT INTO #student VALUES ( ' 李四 ' , ' 化學 ' , 82 )
10  INSERT INTO #student VALUES ( ' 李四 ' , ' 化學 ' , 82 )
11  SELECT  *  FROM #student
 可能很多老手們,一看到這題目就有了答案。當然,貼出答案來不是我的目的,我要帶着SQL新手們重構到答案。用MVP李建忠老師最愛說的話就是------我不建議一上來就套用模式,而應該從重構到模式。

首先大家會想到分兩組

1  select stdname,····,from #student group by stdname
然後······中間該寫什麼呢?

  代碼
1  case  stdsubject when  ' 化學 '  then Result end
2  case  stdsubject when  ' 語文 '  then Result end
3  case  stdsubject when  ' ··· '  then Result end
4  case  stdsubject when  ' ··· '  then Result end
5  case  stdsubject when  ' ··· '  then Result end
表二里面得0是哪裏來的呢?

  代碼
1  isnull(sum( case  stdsubject when  ' 化學 '  then Result end), 0 )
2  isnull(sum( case  stdsubject when  ' 語文 '  then Result end), 0 )
3  isnull(sum( case  stdsubject when  ' ··· '  then Result end), 0 )
4  isnull(sum( case  stdsubject when  ' ··· '  then Result end), 0 )
5  isnull(sum( case  stdsubject when  ' ··· '  then Result end), 0 )
所以得出:

  代碼
1  select stdname,
2  isnull(sum( case  stdsubject when  ' 化學 '  then Result end), 0 ) [化學],
3  isnull(sum( case  stdsubject when  ' 數學 '  then Result end), 0 ) [數學],
4  isnull(sum( case  stdsubject when  ' 物理 '  then Result end), 0 ) [物理],
5  isnull(sum( case  stdsubject when  ' 語文 '  then Result end), 0 ) [語文]
6  from #student
7  group by stdname
然後得出答案:

  代碼
1  declare @sql varchar( 4000 )
2  set  @sql  =   ' select stdname ' 
3  select @sql  =  @sql  +   ' ,isnull(sum(case stdsubject when  ''' + stdsubject + '''  then Result end),0) [ ' + stdsubject + ' ] ' 
4  from (select distinct stdsubject from #student)  as  a
5  select @sql  =  @sql + '  from #student group by stdname ' 
6  print @sql
7  exec(@sql)

壓軸題第二問:把表二轉化爲表一

表一:

 

表二:

 

數據庫代碼如下:

  代碼
1  DROP table #student2
2  CREATE TABLE #student2 (stdname nvarchar( 10 ),化學  int ,數學  int ,物理  int  ,語文  int  )
3  INSERT INTO #student2 VALUES ( ' 李四 ' , 164 , 92 , 82 , 85 )
4  INSERT INTO #student2 VALUES ( ' 張三 ' , 0 , 90 , 85 , 80 )
5  SELECT  *  FROM #student2 
看到這題,直接想到:

  代碼
 1  SELECT ' 李四 ' as  stdname,stdname = ' 化學 ' , 化學  as  result from #student2  where  stdname = ' 李四 '
 2  union all
 3  SELECT ' 李四 ' as  stdname,stdname = ' 數學 ' , 數學  as  result from #student2  where  stdname = ' 李四 '
 4  union all
 5  SELECT ' 李四 ' as  stdname,stdname = ' 物理 ' , 物理  as  result from #student2  where  stdname = ' 李四 '
 6  union all
 7  SELECT ' 李四 ' as  stdname,stdname = ' 語文 ' , 語文  as  result from #student2  where  stdname = ' 李四 '  
  8  union all
 9  SELECT ' 張三 ' as  stdname,stdname = ' 化學 ' , 化學  as  result from #student2  where  stdname = ' 張三 '
10  union all
11  SELECT ' 張三 ' as  stdname,stdname = ' 數學 ' , 數學  as  result from #student2  where  stdname = ' 張三 '
12  union all
13  SELECT ' 張三 ' as  stdname,stdname = ' 物理 ' , 物理  as  result from #student2  where  stdname = ' 張三 '
14  union all
15  SELECT ' 張三 ' as  stdname,stdname = ' 語文 ' , 語文  as  result from #student2  where  stdname = ' 張三 '
 重構到:

  代碼
 1  declare @sql2 varchar( 4000 )
 2    set  @sql2  =   '' 
 3  SELECT @sql2 = @sql2 + 
 4  ' SELECT ''' + stdname + ''' as stdname,stdname= '' 化學 '' , 化學 as result from #student2 where stdname= ''' + stdname + '''
 5  union all
 6  SELECT ''' +stdname+ ''' as  stdname,stdname = '' 數學 '' , 數學  as  result from #student2  where  stdname = ''' +stdname+ '''
 7  union all
 8  SELECT ''' +stdname+ ''' as  stdname,stdname = '' 物理 '' , 物理  as  result from #student2  where  stdname = ''' +stdname+ '''
 9  union all
10  SELECT ''' +stdname+ ''' as  stdname,stdname = '' 語文 '' , 語文  as  result from #student2  where  stdname = ''' +stdname+ '''  union all  '
11  from (SELECT stdname FROM #student2)  as  a
12  SELECT @sql2  =  LEFT(@sql2,LEN(@sql2)  -   10 )
13  PRINT(@sql2)
14  exec(@sql2)
如果要求不能出現  化學  數學  物理 語文 這樣的關鍵字,那麼可以這樣寫:

  代碼
 1  select [name] into #tmpCloumns
 2  from tempdb.dbo.syscolumns
 3  where  id = object_id( ' tempdb.dbo.#student2 ' )
 4  and [name] <> ' stdname '
 5  select  *   from #tmpCloumns
 6 
 7  declare @strSql nvarchar( 800 )
 8  select @strSql = ''
 9  select @strSql = @strSql + ' union all ' + char ( 10 ) + char ( 13 ) +
10                   ' select [stdname], ''' + [name] + '''  as [科目],[ ' + [name] + ' ] ' + char ( 10 ) + char ( 13 ) +
11                   ' from [#student2] ' + char ( 10 ) + char ( 13 )
12  from #tmpCloumns
13 
14  select @strSql = substring(@strSql, 11 ,len(@strSql)) + ' order by stdname,[科目] '
15  -- print @strSql
16  exec(@strsql) 
 這種題目,在各種筆試中出現的概率還是非常大的,大家不用死記。以前有的朋友看着複雜的報表查詢,幾百行SQL,望而生畏,然後說:"這是哪個SQL超人寫的啊!"其實,誰一上來不可能寫出那麼長的SQL,也是慢慢重構--調試--重
 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章