SQL Server 2005 筆試題
Question 1
Can you use a batch SQL or store procedure to calculating the Number of Days in a Month?
I am trying to find a way to calculate the total number of days in each month.
I am trying to avoid using a case statement, as it fails for February on leap years.
Is there a way to do it using the date functions in SQL Server?
*/
--(1)
create proc sp_DayOfthisMonth (@year int , @month int)
as
begin
declare @startDate char(10),@endDate char(10)
set @startDate=cast(@year as char(4))+'-'+cast(@month as char(2))+'-1'
set @endDate=cast(@year as char(4))+'-'+cast(@month+1 as char(2))+'-1'
select datediff(day,cast(@startDate as datetime),cast(@endDate as datetime))
end
理解題意,也想到了使用DateDiff這樣的日期處理函數。
但是由於傳入的參數是整數而非日期類型,當傳入參數錯誤時缺乏對異常的捕獲和控制處理。
例如:
exec sp_DayOfthisMonth 2006, 13
exec sp_DayOfthisMonth 2006, -1 將會發生數據類型轉換錯誤。
並且由於在計算下一月份時沒有使用DateAdd函數,而是簡單的讓@month加1,
當傳入月份參數爲12時將會出錯,也即該存儲過程無法計算12月份的天數。
exec sp_DayOfthisMonth 2006, 12
--(2)
create procedure pro_monthcalucate(@month int,@daysnum int output)
as
if ((@month = 1)or(@month = 3)or(@month = 5)or(@month = 7)or(@month = 8)or(@month = 10)or(@month = 12))
set @daysnum = 31
else
if(@month = 2)
set @daysnum = 28
else
if((@month = 2)or(@month = 4)or(@month = 6)or(@month = 9)or(@month = 11))
set @daysnum = 30
else
begin
print 'the wrong month'
set @daysnum = 0
end
declare @numofmonth int
exec pro_monthcalucate -2,@numofmonth output
print @numofmonth
思維不夠嚴謹,只簡單的想到月份,沒有考慮到閏年的情況。
2004年的2月份是29天,而2006年的2月份只有28天。
對SQL Server的內置日期函數不熟悉,沒有考慮到可以使用DateDiff函數求日期的差值。
編程缺乏技巧,長串的((@month = 1)or(@month = 3)or(@month = 5)or(@month = 7)or(@month = 8)or(@month = 10)or(@month = 12))
可以寫成 @month in (1, 3, 5, 7, 8, 10, 12),代碼簡潔易讀。
-- 窮舉法
create proc sp_getMonthDays @year int
as
select datediff(day,'January 1,2006','February 1,2006') as 'January',
datediff(day,'February 1,'+convert(varchar,@year),'March 1,'+convert(varchar,@year)) as 'February',
datediff(day,'March 1,2006','April 1,2006') as 'March',
datediff(day,'April 1,2006','May 1,2006') as 'April',
datediff(day,'May 1,2006','June 1,2006') as 'May',
datediff(day,'June 1,2006','July 1,2006') as 'June',
datediff(day,'July 1,2006','August 1,2006') as 'July',
datediff(day,'August 1,2006','September 1,2006') as 'August',
datediff(day,'September 1,2006','October 1,2006') as 'September',
datediff(day,'October 1,2006','November 1,2006') as 'October',
datediff(day,'November 1,2006','December 1,2006') as 'November',
datediff(day,'December 1,2006','January 1,2007') as 'December'
--(3)
CREATE FUNCTION fun_daysofmonth (@DATE datetime)
RETURNS int
WITH EXECUTE AS CALLER
AS
BEGIN
DECLARE @daysofmonth int
set @daysofmonth= datediff(day,@Date,dateadd(month,1,@Date))
RETURN(@daysofmonth)
END;
想到了使用DateDiff和DateAdd函數解題,但思維還不夠嚴謹細緻。
當下月天數比上月少時,會出錯。
例如: select dbo.fun_daysofmonth('2006-1-31')
參考答案:
create proc sp_getDaysOfMonth
@dateInput datetime
as
declare @dateFirstDay datetime
set @dateFirstDay = convert(varchar, year(@dateInput)) + '-' + convert(varchar, month(@dateInput)) + '-01'
select month(@dateInput) as 'Month', datediff(day, @dateFirstDay, dateadd(month, 1, @dateFirstDay)) as 'Days'
go
exec sp_getDaysOfMonth '2006-4-6'
/**//**//**//* Question 2
Can you use a SQL statement to calculating it?
How can I print "10 to 20" for books that sell for between $10 and $20,
"unknown" for books whose price is null, and "other" for all other prices?
You can use the table titles in database pubs.
*/
--(1)
select title_id,title,
price = case
when price < 20 and price >10 then '10 to 20'
when price is null then 'unknown'
else 'other'
end
from dbo.titles
大多數的人都忽略了<=和>=。
參考答案:
select title, 'Price Category' =
case
when price between 10 and 20 then '10 to 20'
when price is null then 'unknown'
else 'other'
end
from titles
/**//**//**//* Question 3
Can you use a SQL statement to finding duplicate values?
How can I find authors with the same last name?
You can use the table authors in database pubs. I want to get the result as below:
Output:
au_lname number_dups
---------------------------------------- -----------
Ringer 2
(1 row(s) affected)
*/
--(1)
select au_lname, count(*) number_dups
from authors
group by au_lname
大多數人都只是取出au_lname出現的次數,而沒有使用having子句選擇出重複的au_lname。
--(2)
select au_lname,number_dups
from (select au_lname,number_dups=count(1) from authors group by au_lname) as tmp
where number_dups>=2
雖然答案是正確的,但明顯沒有掌握having子句的用法。
參考答案:
select au_lname, count(*) as 'number_dups'
from authors
group by au_lname
having count(*) > 1
/**//**//**//* Question 4
Can you create a cross-tab report in my SQL Server?
How can I get the report about sale quantity for each store and each quarter
and the total sale quantity for each quarter at year 1993?
You can use the table sales and stores in database pubs.
Table Sales record all sale detail item for each store, column store_id is the id of each store,
ord_date is the order date of each sale item, and column qty is the sale quantity.
Table stores record all store information.
I want to get the result look like as below:
Output:
stor_name Total Qtr1 Qtr2 Qtr3 Qtr4
---------------------------------------- ----------- ----------- -------------------------------
Barnum's 50 0 50 0 0
Bookbeat 55 25 30 0 0
Doc-U-Mat: Quality Laundry and Books 85 0 85 0 0
Fricative Bookshop 60 35 0 0 25
Total 250 60 165 0 25
*/
--(1)
drop table #stor_qty
select isnull(stores.stor_name,'totle') as stor_name,isnull(datename(qq,sales.ord_date),0) as quater,sum(sales.qty) as qty
into #stor_qty
from stores
join sales on stores.stor_id=sales.stor_id
where year(sales.ord_date)=1993
group by (stores.stor_name),datename(qq,sales.ord_date) with cube
select stor_name,isnull([0],0) as 'totle',isnull([1],0) as 'Qtr1',isnull([2],0) as 'Qtr2',isnull([3],0) as 'Qtr3',isnull([4],0) as 'Qtr4',(isnull([1],0)+isnull([2],0)+isnull([3],0)+isnull([4],0)) as 'totle'
from
(select stor_name,qty,quater from #stor_qty
) as pro
pivot
(sum(qty) for quater in([0],[1],[2],[3],[4])) as pvt
基本掌握了with cube和pivot的用法,並會使用,但技巧還有待提升,結果集多了一列total。
在這個查詢中並不需要使用臨時表來完成,用子查詢就可以了。
參考答案:
--SQL Server 2000生成交叉表
SELECT stor_name,
SUM(qty) AS 'Total',
SUM(CASE datepart(qq, ord_date) WHEN 1 THEN qty ELSE 0 END) AS Qtr1,
SUM(CASE datepart(qq, ord_date) WHEN 2 THEN qty ELSE 0 END) AS Qtr2,
SUM(CASE datepart(qq, ord_date) WHEN 3 THEN qty ELSE 0 END) AS Qtr3,
SUM(CASE datepart(qq, ord_date) WHEN 4 THEN qty ELSE 0 END) AS Qtr4
FROM sales s INNER JOIN stores st ON s.stor_id = st.stor_id
WHERE year(ord_date) = 1993
GROUP BY stor_name
Union
SELECT 'Total',
SUM(qty) AS 'Total',
SUM(CASE datepart(qq, ord_date) WHEN 1 THEN qty ELSE 0 END) AS Qtr1,
SUM(CASE datepart(qq, ord_date) WHEN 2 THEN qty ELSE 0 END) AS Qtr2,
SUM(CASE datepart(qq, ord_date) WHEN 3 THEN qty ELSE 0 END) AS Qtr3,
SUM(CASE datepart(qq, ord_date) WHEN 4 THEN qty ELSE 0 END) AS Qtr4
FROM sales s INNER JOIN stores st ON s.stor_id = st.stor_id
WHERE year(ord_date) = 1993
--SQL Server 2005生成交叉表
select stor_name, isnull([0],0) as 'Total',
isnull([1],0) as 'Qtr1',isnull([2],0) as 'Qtr2',
isnull([3],0) as 'Qtr3', isnull([4],0) as 'Qtr4'
from
(
select isnull(t.stor_name, 'Total') as 'stor_name',
isnull(datepart(qq, ord_date),0) as 'Qtr', sum(qty) as 'qty'
from sales s
join stores t on s.stor_id = t.stor_id
where year(s.ord_date) = 1993
group by datepart(qq, ord_date), t.stor_name with cube
) as tmp
pivot
(
sum(qty) for Qtr in ([0], [1], [2], [3], [4])
) as pvt
/**//**//**//* Question 5
How can I add row numbers to my result set?
In database pubs, have a table titles , now I want the result shown as below,
each row have a row number, how can you do that?
Result:
line-no title_id
----------- --------
1 BU1032
2 BU1111
3 BU2075
4 BU7832
5 MC2222
6 MC3021
7 MC3026
8 PC1035
9 PC8888
10 PC9999
11 PS1372
12 PS2091
13 PS2106
14 PS3333
15 PS7777
16 TC3218
17 TC4203
18 TC7777
*/
--(1)
declare @tmp table
(
line_no int identity,
title_id varchar(6)
)
insert into @tmp
select title_id from titles
select * from @tmp
drop table #tmp
select identity(int,1,1) as 'line-no',title_id into #tmp from titles
order by title_id
select * from #tmp
參考答案:
--SQL Server 2000
select count(*) as 'line-no',a.title_id
from titles a join titles b on a.title_id > b.title_id
group by a.title_id
--SQL Server 2005
select row_number() over (order by title_id asc) as 'line-no', title_id
from titles
/**//**//**//* Question 6
How can I list all California authors regardless of whether they have written a book?
In database pubs, have a table authors and titleauthor , table authors has a column state, and titleauhtor have books each
author written.
CA behalf of california in table authors.
*/
--(1)
select au_lname,au_fname,titau.*
from authors au
join titleauthor titau on au.au_id = titau.au_id and au.state = 'CA'
如果使用內連接,那麼就只有在titleauthor表中有記錄的作者纔會被取出。
--(2)
select au_id,au_lname,au_fname from authors a
where state='CA'
and exists(select au_id from titleauthor t
where a.au_id=t.au_id)
select au_lname,au_fname,state
from dbo.authors
where state='CA' and au_id in
(
select au_id from dbo.titleauthor
)
與內連接類似。
參考答案:
select a.au_fname ,a.au_lname, b.title_id
from authors a
left outer join titleauthor as b on a.au_id = b.au_id
where a.state = 'CA'
/**//**//**//* Question 7
How can I get a list of the stores that have bought both 'bussiness' and 'mod_cook' type books?
In database pubs, use three table stores,sales and titles to implement this requestment.
Now I want to get the result as below:
stor_id stor_name
------- ----------------------------------------
...
7896 Fricative Bookshop
...
...
...
*/
-- score : 0
select st.stor_id,st.stor_name
from dbo.stores st
join dbo.sales sl on st.stor_id = sl.stor_id
join dbo.titles tl on sl.title_id = tl.title_id and (tl.type = 'business' and tl.type = 'trad_cook')
-- score : 0
select distinct a.stor_id , a.stor_name
from stores a
inner join sales b on(a.stor_id = b.stor_id)
inner join titles c on(b.title_id = c.title_id and c.type = 'bussiness' or c.type = 'mod_cook')
select sal.stor_id, stor_name
from sales sal
join stores sto on sal.stor_id = sto.stor_id
join titles til on sal.title_id = til.title_id
where til.[type] = 'bussiness' or til.[type] ='mod_cook'
select distinct stores.stor_id,stor_name
from stores
inner join sales on stores.stor_id = sales.stor_id
inner join titles on sales.title_id = titles.title_id
where type in ('business','mod_cook')
-- another one , score: 0
begin tran Question7
select stor_id, stor_name
from stores
where stor_id in (
select stor_id
from sales
where title_id in (
select title_id
from titles
where type = 'business' or type = 'mod_cook'
)
)
commit tran Question7
-- score: 6
with Stor_TilteType(stor_id,stor_name,title_type) as
(select st.stor_id,st.stor_name,ti.type
from stores st
join sales sa on st.stor_id=sa.stor_id
join titles ti on sa.title_id=ti.title_id
)
select distinct stor_id,stor_name
from Stor_TilteType
where title_type='business'
and stor_id in(select stor_id
from Stor_TilteType
where title_type='mod_cook'
)
-- score: 8
select distinct stores.stor_id, stor_name
from stores
inner join sales on stores.stor_id = sales.stor_id
inner join titles on sales.title_id = titles.title_id
and titles.type = 'business'
intersect
select distinct stores.stor_id, stor_name
from stores
inner join sales on stores.stor_id = sales.stor_id
inner join titles on sales.title_id = titles.title_id
and titles.type = 'mod_cook'
-- score: 10
select distinct st.stor_id,st.stor_name
from stores st
inner join sales ss1 on ss1.stor_id = st.stor_id
inner join titles tit1 on ss1.title_id = tit1.title_id
where tit1.[type] ='business' and exists
(
select *
from sales ss2
inner join titles tit2 on ss2.title_id = tit2.title_id
where st.stor_id = ss2.stor_id and tit2.[type] ='mod_cook'
)
-- 參考答案
select s.stor_id, st.stor_name
from sales s
join titles t on s.title_id = t.title_id and t.type in ('mod_cook', 'business')
join stores st on s.stor_id = st.stor_id
group by s.stor_id, st.stor_name
having count(distinct t.type) = 2
select stor_id, stor_name from
stores
where
exists
(
select * from
sales join titles on sales.title_id = titles.title_id
where titles.[type] = 'business'
and stores.stor_id = sales.stor_id
)
and
exists
(
select * from
sales join titles on sales.title_id = titles.title_id
where titles.[type] = 'mod_cook'
and stores.stor_id = sales.stor_id
)
/**//**//**//* Question 8
How can I list non-contignous data?
In database pubs, I create a table test using statement as below, and I insert several row as below
create table test
( id int primary key )
go
insert into test values (1 )
insert into test values (2 )
insert into test values (3 )
insert into test values (4 )
insert into test values (5 )
insert into test values (6 )
insert into test values (8 )
insert into test values (9 )
insert into test values (11)
insert into test values (12)
insert into test values (13)
insert into test values (14)
insert into test values (18)
insert into test values (19)
go
Now I want to list the result of the non-contignous row as below,how can I do it?
Missing after Missing before
------------- --------------
6 8
9 11
...
*/
-- 2
declare @temp table (id int)
declare @count int
set @count=1
while @count<=20
begin
insert into @temp values(@count)
set @count=@count+1
end
select id-1 as 'Missing after',id+1 as 'Missing before'
from(select id from @temp
except
select id from test
) as t
select id as 'Missing after', id+2 as 'Missing before '
from test t1
where not exists
(
select * from test t2
where t1.id = t2.id-1
)
-- score: 6
create proc proc_ShowMissing
as
declare @MissingInfo table
(
Missing_after int primary key,
Missing_before int
)
declare @num_1 int, @num_2 int
declare cNum cursor FAST_FORWARD
for
select id from test
open cNum
fetch cNum into @num_1
fetch cNum into @num_2
-- print cast(@num_1 as varchar(10)) + '''' + cast(@num_2 as varchar(10))
while @@Fetch_Status = 0
begin
if (@num_1+1) <> (@num_2)
begin
insert into @MissingInfo values (@num_1, @num_2)
end
else
print 'contignous'
set @num_1 = @num_2
fetch cNum into @num_2
end
close cNum
deallocate cNum
select * from @MissingInfo
execute proc_ShowMissing
--
print('Missing after Missing before')
print('------------- -------------- ')
declare @iLast int,@iNext int,@iCurCount int
declare cTest CURSOR FAST_FORWARD
for select id
from test
open cTest
fetch cTest into @iCurCount
set @iLast = @iCurCount
while @@Fetch_Status = 0
begin
set @iNext = @iCurCount
if @iLast+1 <> @iNext and @iLast <> @iNext
print cast(@iLast as varchar)+' '+cast(@iNext as varchar)
set @iLast = @iNext
fetch cTest into @iCurCount
end
close cTest
deallocate cTest
-- score:10
select temp.[Missing after],min(test.ID) as [Missing before] from
(
select ID as [Missing after] from test
where ID + 1 < (select min(ID) from test t2 where t2.ID > test.ID)
) as temp
join test on temp.[Missing after] < test.ID
group by temp.[Missing after]
-- score: 10
select t1.id as 'Missing after',t2.id as 'Minssing before' from
(
select row_number() over (order by id) as 'row_number',id
from test)as t1
join (select row_number() over (order by id) as 'row_number',id
from test) as t2
on t1.row_number=t2.row_number-1
where t1.id<>t2.id-1
-- 參考答案
select max(a.id) as "Missing After",
min(b.id) as "Missing Before"
from test a ,test b
where a.id<b.id
group by b.id
having min(b.id)-max(a.id)>1
/**//**//**//* Question 9
How can I list all book with prices greather than the average price of books of the same type?
In database pubs, have a table named titles , its column named price mean the price of the book,
and another named type mean the type of books.
Now I want to get the result as below:
type title price
------------ ---------------------------------------------------------------------------------
business The Busy Executive's Database Guide 19.9900
...
...
...
...
*/
--(1)
Select type,price,title
from titles
Where Price > (select avg(price) from titles)
這樣取得的是所有書的平均價格,而不是某一類書的平均價格。
--(2)
with avgprice(type,price) as
(
select type,avg(price) as price
from titles t2
group by type
)
select distinct titles.type,titles.title,titles.price
from titles
inner join avgprice on (titles.type = avgprice.type and titles.price> avgprice.price)
使用CTE也可以實現,但如果取出的數據量很大會影響性能。
爲什麼要用distinct呢?
--(3)
select a.type, a.title, a.price
from titles a
where a.price > (
select avg (isnull (price, 0)) -- 有價格爲null的情況,算平均值時應該算上價格爲null的書本
from titles b
where a.type = b.type
)
因爲沒有具體提及統計的需求,但這樣嚴謹的作風非常值得大家學習。
參考答案:
select t.type, t.title, t.price
from titles t
where t.price >
(select avg(price) from titles tt where tt.type = t.type)
Can you use a batch SQL or store procedure to calculating the Number of Days in a Month?
I am trying to find a way to calculate the total number of days in each month.
I am trying to avoid using a case statement, as it fails for February on leap years.
Is there a way to do it using the date functions in SQL Server?
*/
--(1)
create proc sp_DayOfthisMonth (@year int , @month int)
as
begin
declare @startDate char(10),@endDate char(10)
set @startDate=cast(@year as char(4))+'-'+cast(@month as char(2))+'-1'
set @endDate=cast(@year as char(4))+'-'+cast(@month+1 as char(2))+'-1'
select datediff(day,cast(@startDate as datetime),cast(@endDate as datetime))
end
理解題意,也想到了使用DateDiff這樣的日期處理函數。
但是由於傳入的參數是整數而非日期類型,當傳入參數錯誤時缺乏對異常的捕獲和控制處理。
例如:
exec sp_DayOfthisMonth 2006, 13
exec sp_DayOfthisMonth 2006, -1 將會發生數據類型轉換錯誤。
並且由於在計算下一月份時沒有使用DateAdd函數,而是簡單的讓@month加1,
當傳入月份參數爲12時將會出錯,也即該存儲過程無法計算12月份的天數。
exec sp_DayOfthisMonth 2006, 12
--(2)
create procedure pro_monthcalucate(@month int,@daysnum int output)
as
if ((@month = 1)or(@month = 3)or(@month = 5)or(@month = 7)or(@month = 8)or(@month = 10)or(@month = 12))
set @daysnum = 31
else
if(@month = 2)
set @daysnum = 28
else
if((@month = 2)or(@month = 4)or(@month = 6)or(@month = 9)or(@month = 11))
set @daysnum = 30
else
begin
print 'the wrong month'
set @daysnum = 0
end
declare @numofmonth int
exec pro_monthcalucate -2,@numofmonth output
print @numofmonth
思維不夠嚴謹,只簡單的想到月份,沒有考慮到閏年的情況。
2004年的2月份是29天,而2006年的2月份只有28天。
對SQL Server的內置日期函數不熟悉,沒有考慮到可以使用DateDiff函數求日期的差值。
編程缺乏技巧,長串的((@month = 1)or(@month = 3)or(@month = 5)or(@month = 7)or(@month = 8)or(@month = 10)or(@month = 12))
可以寫成 @month in (1, 3, 5, 7, 8, 10, 12),代碼簡潔易讀。
-- 窮舉法
create proc sp_getMonthDays @year int
as
select datediff(day,'January 1,2006','February 1,2006') as 'January',
datediff(day,'February 1,'+convert(varchar,@year),'March 1,'+convert(varchar,@year)) as 'February',
datediff(day,'March 1,2006','April 1,2006') as 'March',
datediff(day,'April 1,2006','May 1,2006') as 'April',
datediff(day,'May 1,2006','June 1,2006') as 'May',
datediff(day,'June 1,2006','July 1,2006') as 'June',
datediff(day,'July 1,2006','August 1,2006') as 'July',
datediff(day,'August 1,2006','September 1,2006') as 'August',
datediff(day,'September 1,2006','October 1,2006') as 'September',
datediff(day,'October 1,2006','November 1,2006') as 'October',
datediff(day,'November 1,2006','December 1,2006') as 'November',
datediff(day,'December 1,2006','January 1,2007') as 'December'
--(3)
CREATE FUNCTION fun_daysofmonth (@DATE datetime)
RETURNS int
WITH EXECUTE AS CALLER
AS
BEGIN
DECLARE @daysofmonth int
set @daysofmonth= datediff(day,@Date,dateadd(month,1,@Date))
RETURN(@daysofmonth)
END;
想到了使用DateDiff和DateAdd函數解題,但思維還不夠嚴謹細緻。
當下月天數比上月少時,會出錯。
例如: select dbo.fun_daysofmonth('2006-1-31')
參考答案:
create proc sp_getDaysOfMonth
@dateInput datetime
as
declare @dateFirstDay datetime
set @dateFirstDay = convert(varchar, year(@dateInput)) + '-' + convert(varchar, month(@dateInput)) + '-01'
select month(@dateInput) as 'Month', datediff(day, @dateFirstDay, dateadd(month, 1, @dateFirstDay)) as 'Days'
go
exec sp_getDaysOfMonth '2006-4-6'
/**//**//**//* Question 2
Can you use a SQL statement to calculating it?
How can I print "10 to 20" for books that sell for between $10 and $20,
"unknown" for books whose price is null, and "other" for all other prices?
You can use the table titles in database pubs.
*/
--(1)
select title_id,title,
price = case
when price < 20 and price >10 then '10 to 20'
when price is null then 'unknown'
else 'other'
end
from dbo.titles
大多數的人都忽略了<=和>=。
參考答案:
select title, 'Price Category' =
case
when price between 10 and 20 then '10 to 20'
when price is null then 'unknown'
else 'other'
end
from titles
/**//**//**//* Question 3
Can you use a SQL statement to finding duplicate values?
How can I find authors with the same last name?
You can use the table authors in database pubs. I want to get the result as below:
Output:
au_lname number_dups
---------------------------------------- -----------
Ringer 2
(1 row(s) affected)
*/
--(1)
select au_lname, count(*) number_dups
from authors
group by au_lname
大多數人都只是取出au_lname出現的次數,而沒有使用having子句選擇出重複的au_lname。
--(2)
select au_lname,number_dups
from (select au_lname,number_dups=count(1) from authors group by au_lname) as tmp
where number_dups>=2
雖然答案是正確的,但明顯沒有掌握having子句的用法。
參考答案:
select au_lname, count(*) as 'number_dups'
from authors
group by au_lname
having count(*) > 1
/**//**//**//* Question 4
Can you create a cross-tab report in my SQL Server?
How can I get the report about sale quantity for each store and each quarter
and the total sale quantity for each quarter at year 1993?
You can use the table sales and stores in database pubs.
Table Sales record all sale detail item for each store, column store_id is the id of each store,
ord_date is the order date of each sale item, and column qty is the sale quantity.
Table stores record all store information.
I want to get the result look like as below:
Output:
stor_name Total Qtr1 Qtr2 Qtr3 Qtr4
---------------------------------------- ----------- ----------- -------------------------------
Barnum's 50 0 50 0 0
Bookbeat 55 25 30 0 0
Doc-U-Mat: Quality Laundry and Books 85 0 85 0 0
Fricative Bookshop 60 35 0 0 25
Total 250 60 165 0 25
*/
--(1)
drop table #stor_qty
select isnull(stores.stor_name,'totle') as stor_name,isnull(datename(qq,sales.ord_date),0) as quater,sum(sales.qty) as qty
into #stor_qty
from stores
join sales on stores.stor_id=sales.stor_id
where year(sales.ord_date)=1993
group by (stores.stor_name),datename(qq,sales.ord_date) with cube
select stor_name,isnull([0],0) as 'totle',isnull([1],0) as 'Qtr1',isnull([2],0) as 'Qtr2',isnull([3],0) as 'Qtr3',isnull([4],0) as 'Qtr4',(isnull([1],0)+isnull([2],0)+isnull([3],0)+isnull([4],0)) as 'totle'
from
(select stor_name,qty,quater from #stor_qty
) as pro
pivot
(sum(qty) for quater in([0],[1],[2],[3],[4])) as pvt
基本掌握了with cube和pivot的用法,並會使用,但技巧還有待提升,結果集多了一列total。
在這個查詢中並不需要使用臨時表來完成,用子查詢就可以了。
參考答案:
--SQL Server 2000生成交叉表
SELECT stor_name,
SUM(qty) AS 'Total',
SUM(CASE datepart(qq, ord_date) WHEN 1 THEN qty ELSE 0 END) AS Qtr1,
SUM(CASE datepart(qq, ord_date) WHEN 2 THEN qty ELSE 0 END) AS Qtr2,
SUM(CASE datepart(qq, ord_date) WHEN 3 THEN qty ELSE 0 END) AS Qtr3,
SUM(CASE datepart(qq, ord_date) WHEN 4 THEN qty ELSE 0 END) AS Qtr4
FROM sales s INNER JOIN stores st ON s.stor_id = st.stor_id
WHERE year(ord_date) = 1993
GROUP BY stor_name
Union
SELECT 'Total',
SUM(qty) AS 'Total',
SUM(CASE datepart(qq, ord_date) WHEN 1 THEN qty ELSE 0 END) AS Qtr1,
SUM(CASE datepart(qq, ord_date) WHEN 2 THEN qty ELSE 0 END) AS Qtr2,
SUM(CASE datepart(qq, ord_date) WHEN 3 THEN qty ELSE 0 END) AS Qtr3,
SUM(CASE datepart(qq, ord_date) WHEN 4 THEN qty ELSE 0 END) AS Qtr4
FROM sales s INNER JOIN stores st ON s.stor_id = st.stor_id
WHERE year(ord_date) = 1993
--SQL Server 2005生成交叉表
select stor_name, isnull([0],0) as 'Total',
isnull([1],0) as 'Qtr1',isnull([2],0) as 'Qtr2',
isnull([3],0) as 'Qtr3', isnull([4],0) as 'Qtr4'
from
(
select isnull(t.stor_name, 'Total') as 'stor_name',
isnull(datepart(qq, ord_date),0) as 'Qtr', sum(qty) as 'qty'
from sales s
join stores t on s.stor_id = t.stor_id
where year(s.ord_date) = 1993
group by datepart(qq, ord_date), t.stor_name with cube
) as tmp
pivot
(
sum(qty) for Qtr in ([0], [1], [2], [3], [4])
) as pvt
/**//**//**//* Question 5
How can I add row numbers to my result set?
In database pubs, have a table titles , now I want the result shown as below,
each row have a row number, how can you do that?
Result:
line-no title_id
----------- --------
1 BU1032
2 BU1111
3 BU2075
4 BU7832
5 MC2222
6 MC3021
7 MC3026
8 PC1035
9 PC8888
10 PC9999
11 PS1372
12 PS2091
13 PS2106
14 PS3333
15 PS7777
16 TC3218
17 TC4203
18 TC7777
*/
--(1)
declare @tmp table
(
line_no int identity,
title_id varchar(6)
)
insert into @tmp
select title_id from titles
select * from @tmp
drop table #tmp
select identity(int,1,1) as 'line-no',title_id into #tmp from titles
order by title_id
select * from #tmp
參考答案:
--SQL Server 2000
select count(*) as 'line-no',a.title_id
from titles a join titles b on a.title_id > b.title_id
group by a.title_id
--SQL Server 2005
select row_number() over (order by title_id asc) as 'line-no', title_id
from titles
/**//**//**//* Question 6
How can I list all California authors regardless of whether they have written a book?
In database pubs, have a table authors and titleauthor , table authors has a column state, and titleauhtor have books each
author written.
CA behalf of california in table authors.
*/
--(1)
select au_lname,au_fname,titau.*
from authors au
join titleauthor titau on au.au_id = titau.au_id and au.state = 'CA'
如果使用內連接,那麼就只有在titleauthor表中有記錄的作者纔會被取出。
--(2)
select au_id,au_lname,au_fname from authors a
where state='CA'
and exists(select au_id from titleauthor t
where a.au_id=t.au_id)
select au_lname,au_fname,state
from dbo.authors
where state='CA' and au_id in
(
select au_id from dbo.titleauthor
)
與內連接類似。
參考答案:
select a.au_fname ,a.au_lname, b.title_id
from authors a
left outer join titleauthor as b on a.au_id = b.au_id
where a.state = 'CA'
/**//**//**//* Question 7
How can I get a list of the stores that have bought both 'bussiness' and 'mod_cook' type books?
In database pubs, use three table stores,sales and titles to implement this requestment.
Now I want to get the result as below:
stor_id stor_name
------- ----------------------------------------
...
7896 Fricative Bookshop
...
...
...
*/
-- score : 0
select st.stor_id,st.stor_name
from dbo.stores st
join dbo.sales sl on st.stor_id = sl.stor_id
join dbo.titles tl on sl.title_id = tl.title_id and (tl.type = 'business' and tl.type = 'trad_cook')
-- score : 0
select distinct a.stor_id , a.stor_name
from stores a
inner join sales b on(a.stor_id = b.stor_id)
inner join titles c on(b.title_id = c.title_id and c.type = 'bussiness' or c.type = 'mod_cook')
select sal.stor_id, stor_name
from sales sal
join stores sto on sal.stor_id = sto.stor_id
join titles til on sal.title_id = til.title_id
where til.[type] = 'bussiness' or til.[type] ='mod_cook'
select distinct stores.stor_id,stor_name
from stores
inner join sales on stores.stor_id = sales.stor_id
inner join titles on sales.title_id = titles.title_id
where type in ('business','mod_cook')
-- another one , score: 0
begin tran Question7
select stor_id, stor_name
from stores
where stor_id in (
select stor_id
from sales
where title_id in (
select title_id
from titles
where type = 'business' or type = 'mod_cook'
)
)
commit tran Question7
-- score: 6
with Stor_TilteType(stor_id,stor_name,title_type) as
(select st.stor_id,st.stor_name,ti.type
from stores st
join sales sa on st.stor_id=sa.stor_id
join titles ti on sa.title_id=ti.title_id
)
select distinct stor_id,stor_name
from Stor_TilteType
where title_type='business'
and stor_id in(select stor_id
from Stor_TilteType
where title_type='mod_cook'
)
-- score: 8
select distinct stores.stor_id, stor_name
from stores
inner join sales on stores.stor_id = sales.stor_id
inner join titles on sales.title_id = titles.title_id
and titles.type = 'business'
intersect
select distinct stores.stor_id, stor_name
from stores
inner join sales on stores.stor_id = sales.stor_id
inner join titles on sales.title_id = titles.title_id
and titles.type = 'mod_cook'
-- score: 10
select distinct st.stor_id,st.stor_name
from stores st
inner join sales ss1 on ss1.stor_id = st.stor_id
inner join titles tit1 on ss1.title_id = tit1.title_id
where tit1.[type] ='business' and exists
(
select *
from sales ss2
inner join titles tit2 on ss2.title_id = tit2.title_id
where st.stor_id = ss2.stor_id and tit2.[type] ='mod_cook'
)
-- 參考答案
select s.stor_id, st.stor_name
from sales s
join titles t on s.title_id = t.title_id and t.type in ('mod_cook', 'business')
join stores st on s.stor_id = st.stor_id
group by s.stor_id, st.stor_name
having count(distinct t.type) = 2
select stor_id, stor_name from
stores
where
exists
(
select * from
sales join titles on sales.title_id = titles.title_id
where titles.[type] = 'business'
and stores.stor_id = sales.stor_id
)
and
exists
(
select * from
sales join titles on sales.title_id = titles.title_id
where titles.[type] = 'mod_cook'
and stores.stor_id = sales.stor_id
)
/**//**//**//* Question 8
How can I list non-contignous data?
In database pubs, I create a table test using statement as below, and I insert several row as below
create table test
( id int primary key )
go
insert into test values (1 )
insert into test values (2 )
insert into test values (3 )
insert into test values (4 )
insert into test values (5 )
insert into test values (6 )
insert into test values (8 )
insert into test values (9 )
insert into test values (11)
insert into test values (12)
insert into test values (13)
insert into test values (14)
insert into test values (18)
insert into test values (19)
go
Now I want to list the result of the non-contignous row as below,how can I do it?
Missing after Missing before
------------- --------------
6 8
9 11
...
*/
-- 2
declare @temp table (id int)
declare @count int
set @count=1
while @count<=20
begin
insert into @temp values(@count)
set @count=@count+1
end
select id-1 as 'Missing after',id+1 as 'Missing before'
from(select id from @temp
except
select id from test
) as t
select id as 'Missing after', id+2 as 'Missing before '
from test t1
where not exists
(
select * from test t2
where t1.id = t2.id-1
)
-- score: 6
create proc proc_ShowMissing
as
declare @MissingInfo table
(
Missing_after int primary key,
Missing_before int
)
declare @num_1 int, @num_2 int
declare cNum cursor FAST_FORWARD
for
select id from test
open cNum
fetch cNum into @num_1
fetch cNum into @num_2
-- print cast(@num_1 as varchar(10)) + '''' + cast(@num_2 as varchar(10))
while @@Fetch_Status = 0
begin
if (@num_1+1) <> (@num_2)
begin
insert into @MissingInfo values (@num_1, @num_2)
end
else
print 'contignous'
set @num_1 = @num_2
fetch cNum into @num_2
end
close cNum
deallocate cNum
select * from @MissingInfo
execute proc_ShowMissing
--
print('Missing after Missing before')
print('------------- -------------- ')
declare @iLast int,@iNext int,@iCurCount int
declare cTest CURSOR FAST_FORWARD
for select id
from test
open cTest
fetch cTest into @iCurCount
set @iLast = @iCurCount
while @@Fetch_Status = 0
begin
set @iNext = @iCurCount
if @iLast+1 <> @iNext and @iLast <> @iNext
print cast(@iLast as varchar)+' '+cast(@iNext as varchar)
set @iLast = @iNext
fetch cTest into @iCurCount
end
close cTest
deallocate cTest
-- score:10
select temp.[Missing after],min(test.ID) as [Missing before] from
(
select ID as [Missing after] from test
where ID + 1 < (select min(ID) from test t2 where t2.ID > test.ID)
) as temp
join test on temp.[Missing after] < test.ID
group by temp.[Missing after]
-- score: 10
select t1.id as 'Missing after',t2.id as 'Minssing before' from
(
select row_number() over (order by id) as 'row_number',id
from test)as t1
join (select row_number() over (order by id) as 'row_number',id
from test) as t2
on t1.row_number=t2.row_number-1
where t1.id<>t2.id-1
-- 參考答案
select max(a.id) as "Missing After",
min(b.id) as "Missing Before"
from test a ,test b
where a.id<b.id
group by b.id
having min(b.id)-max(a.id)>1
/**//**//**//* Question 9
How can I list all book with prices greather than the average price of books of the same type?
In database pubs, have a table named titles , its column named price mean the price of the book,
and another named type mean the type of books.
Now I want to get the result as below:
type title price
------------ ---------------------------------------------------------------------------------
business The Busy Executive's Database Guide 19.9900
...
...
...
...
*/
--(1)
Select type,price,title
from titles
Where Price > (select avg(price) from titles)
這樣取得的是所有書的平均價格,而不是某一類書的平均價格。
--(2)
with avgprice(type,price) as
(
select type,avg(price) as price
from titles t2
group by type
)
select distinct titles.type,titles.title,titles.price
from titles
inner join avgprice on (titles.type = avgprice.type and titles.price> avgprice.price)
使用CTE也可以實現,但如果取出的數據量很大會影響性能。
爲什麼要用distinct呢?
--(3)
select a.type, a.title, a.price
from titles a
where a.price > (
select avg (isnull (price, 0)) -- 有價格爲null的情況,算平均值時應該算上價格爲null的書本
from titles b
where a.type = b.type
)
因爲沒有具體提及統計的需求,但這樣嚴謹的作風非常值得大家學習。
參考答案:
select t.type, t.title, t.price
from titles t
where t.price >
(select avg(price) from titles tt where tt.type = t.type)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.