15

データベース システム試験 (明日) の勉強をしていますが、クエリを書くように求められる演習で問題があります。次に例を示します。

ここに画像の説明を入力

次の質問に答えるクエリを書くように求められています: 年齢が最も低い著者の中で、最も多くの本を書いたのは誰ですか?

問題は、先生が FROM 句内でサブクエリを使用すること、および TOP を使用することを禁止していることです

私は答えを書きましたが、私が知っているものは間違っています:

SELECT W.AName, COUNT(W.ID_B) AS NBooks
FROM Write W, Author A1
WHERE (A1.AName = W.AName) AND
      (A1.AAge = (SELECT MIN(A2.Age)
                  FROM Author A2))
GROUP BY W.AName
ORDER BY NBooks DESC

これは、年齢が低いすべての著者と、それぞれの書かれた本の数を示しています(願っています..)。正解は、この行の最初の行のみのはずです。

はっきりさせてください:

Table Author
AName    | AAge
---------------
John     | 25
Rick     | 30
Sean     | 26
Lena     | 25

Table Writes
AName    | ID_B
---------------
John     | 2
Lena     | 1
John     | 3
Lena     | 4
Rick     | 5
Rick     | 6
Lena     | 6
Rick     | 7
Rick     | 8

(Sean は本を書いていないことに注意してください。本 nº6 には 2 人の著者がいて、Rick はほとんどの本の著者です (4) )。

今、私が上で書いたコードはこの結果を与えます(私は推測します):

AName    | NBooks
-----------------
Lena     | 3
John     | 2

(最低年齢は 25 歳で、レナとジョンは両方とも 25 歳です)

質問される内容は次のとおりです。

AName    | NBooks
-----------------
Lena     | 3

(Lena は著者であり、すべての著者の中で最も年齢が低く (25)、ほとんどの本が書かれています)

前もって感謝します

4

5 に答える 5

5

あなたは学生なので、質問の一部にお答えします。最年少の部分を無視した答えは次のとおりです。

select a.AName, COUNT(*) as NumBooks
from Author a join
     Write w
     on a.AName = w.AName
group by a.AName
having count(*) >= all(select COUNT(*) as NumBooks
                       from write w
                       group by w.AName
                      )

改造方法はお分かりいただけると思います。

ちなみに、limitandの制限topは、この例のためだけのものだと思います。それ以外の場合、これらは非常に重要な構成要素であるため、別の教師を取得する必要があります。

,また、from句ではなく、従来の結合構文を学習する必要があります。もう一度言いますが、あなたの先生が (1988 年頃から) 現代の文法を教えていない場合は、新しい先生を見つけてください。また、サブクエリの制限は CTE にも適用されると思います。

また、クエリの「正しい」バージョンを指摘したいと思います。

select top 1 a.aname, count(*) as NumBooks
from Author a join
     Write w
     on a.AName = w.AName
group by author.name, author.Aage
order by author.Age asc, count(*) desc

このクエリは、ほぼすべてのディメンションで上記のクエリよりも優れています。それは1つjoin、1つ、group byそして1つのソートを行います。私のクエリの完全なバージョンは、2 つjoinの s を明示的に実行し、2 つjoinの s を暗黙的に (age 句)、2 つgroup byの s を実行します。前者は後者よりも優れたパフォーマンスを発揮します。

読みやすさの観点から、このバージョンは短くてきれいです。また、最初のバージョンの「異常な」構造よりも、これが何をしているのかを教える方がはるかに簡単だと思います。topほとんどの生徒は、何をしているのかを理解し、order byこれをエミュレートできます。その句で何が起こるかを模倣するにhavingは、精神的な体操が必要です。

最大数のすべての著者を取得したい場合、最初に、前のクエリが次と同等であることを認識する必要があります。

select aname, NumBooks
from (select a.aname, count(*) as NumBooks,
             row_number() over (partition by author.Name order by a.aAge, count(*) desc) as seqnum
      from Author a join
           Write w
           on a.AName = w.AName
      group by author.name, author.Aage
     ) aw
where seqnum = 1

これを切り替えてすべての著者を取得するのは簡単です:

select aname, NumBooks
from (select a.aname, count(*) as NumBooks,
             dense_rank() over (partition by author.Name order by a.aAge, count(*) desc) as seqnum
      from Author a join
           Write w
           on a.AName = w.AName
      group by author.name, author.Aage
     ) aw
where seqnum = 1

これは、質問に答えるクエリよりも効率的です。top節でor サブクエリを使用できないことfromは、二人三脚で走るようなものです。はい、おそらくそこにたどり着くことができますが、自分の 2 本足で走った方がはるかに速くたどり着くことができます。

于 2013-01-11T21:23:26.443 に答える
2

これはある程度の制限ですが、創造性を活かすことができます。

したがって、最も若い作家の 1 人が必要であり、別の最年少の作家によって書かれた本よりも多い (または等しい) 本の数を書いている.

SELECT
  [a1].[AName],
  [a1].[AAge],
  COUNT(*) AS [NBooks]
FROM [Author] [a1], [Writes] [w1]
WHERE 
  [a1].[AName] = [w1].[AName]
  AND [a1].[AAge] = (SELECT MIN([a2].[AAge]) FROM [Author] [a2])
GROUP BY 
  [a1].[AName],
  [a1].[AAge]
HAVING COUNT(*) >= ALL
  (SELECT
    COUNT(*) AS [NBooks]
  FROM [Author] [a3], [Writes] [w2]
  WHERE 
    [a3].[AName] = [w2].[AName]
    AND [a3].[AAge] = (SELECT MIN([a4].[AAge]) FROM [Author] [a4])
    AND [a3].[AName] <> [a1].[AName]
  GROUP BY 
    [a3].[AName],
    [a3].[AAge])

PS: 認めざるを得ません。私はゴードン リノフALLから学びました。

于 2013-01-11T21:50:06.950 に答える
1

結果を1つだけにしたい場合は、一番上の結果を選択すると、残りは注文で処理されます。私は個人的にランキング関数を実行して、Aggregate()Over()ウィンドウ関数を使用してランキングを明示的に取得します。しかし、あなたが学んでいるので、おそらく彼らはそれをまだ持ち出して、「トップ」がどのように機能するかをあなたに見せたくないでしょう。

declare @Person Table ( personID int identity, person varchar(8), age int);

insert into @Person values ('Brett', 34),('John', 34),('Peter', 52);

declare @Books Table ( BookID int identity, personID int);

insert into @Books values (1),(1),(1),(2),(2),(3)

Select top 1 -- TOP WILL LIMIT TO CHOICE YOU WANT BASED ON ORDER BY CLAUSE
    p.person
,   p.age
,   count(b.BookID) as cnts
from @Person p, @Books b
where p.personID = b.personID
group by p.person, p.age
order by age, cnts desc
于 2013-01-11T20:40:22.170 に答える
1

結果として1行だけが必要なことを理解しています。

最初に著者を制限してから、内部結合を使用して、書き込みテーブルから著者の名前と本の数を取得できます。

SELECT W.AName, COUNT(W.ID_B) AS NBooks
FROM Write W INNER JOIN Author A1 ON A1.AName = W.AName
WHERE 
A1.AName = (SELECT AName FROM Write GROUP BY AName ORDER BY COUNT(ID_B) DESC)
AND A1.AAge = (SELECT MIN(A2.Age) FROM Author A2)
GROUP BY W.AName
ORDER BY NBooks DESC
于 2013-01-11T20:59:45.273 に答える
1

CTEの使用が許可されており、そのtrivalをランク付けしている場合。

WITH cte 
     AS (SELECT a.aname, 
                A.aage, 
                Count(id_b)                             Book_Count, 
                RANK() 
                  OVER( 
                    ORDER BY a.aage, Count(id_b) DESC ) rn 
         FROM   author a 
                INNER JOIN writes w 
                        ON a.aname = w.aname 
         GROUP  BY a.aname, 
                   a.aage) 
SELECT aname, 
       Book_Count
FROM   cte 
WHERE  rn = 1 

SQL フィドル

ジョンが別の本を書いているデモ

于 2013-01-11T21:03:26.540 に答える