2016-03-29 11 views
0

ゴールを挿入することはできませんMERGE、UPDATE、INSERT T-SQLの値NULL

DateTXTは動的変数で、コードを実行している月を返します。

必要条件:コードを一度実行してテーブルを作成できる場所にMERGE、UPDATE、INSERT関数を使用しようとしています。

はその後毎月私は新しい月のコラムに永久的なテーブルを追加します。

EXEC('ALTER TABLE StudentRanking 
    ADD ' + @DateTXT + ' smallint NOT NULL DEFAULT(999)') 

| StudentID | Jan | Feb | 
| 56789 | 2 | 999 | 
| 12345 | 7 | 999 | 

私は2月のために再び順位コードを実行し、私がマージするために使用する一時テーブル、にそれを保存します、更新、StudentRankingテーブルに挿入します。

EXEC(' 
    MERGE StudentRanking AS TARGET 
    USING ##TEMPDB2 AS SOURCE ON (TARGET.StudentID = SOURCE.StudentID) 

    WHEN MATCHED AND TARGET.' + @DateTXT + ' <> SOURCE.' + @DateTXT + ' 
    THEN UPDATE SET TARGET.' + @DateTXT + ' = SOURCE.' + @DateTXT + ' 

    WHEN NOT MATCHED BY TARGET THEN 
    INSERT (StudentID, ' + @Rank_TXT + ') 
    VALUES (SOURCE.StudentID, SOURCE.' + @Rank_TXT + ') ') 

| StudentID | Jan | Feb | 
| 56789 | 2 | 3 | 
| 12345 | 7 |null | 

| StudentID | Feb | 
| 56789 | 3 | 

(note.. student 12345 doesn't come up) 

は、だから私は実行しているリストで終わるしたいのですが

問題:生徒の中には学校を去る人がいるため、いくつかの生徒は学校を去ります。 12345)が2月にはランクを持っていないので、私は一時テーブルから結果を挿入しようとすると、私はこのエラーを取得する:私はISNULL(ランキングを行うことができ

SQL Server Database Error: Cannot insert the value NULL into column 'Feb', table 'tempdb.dbo.##TEMPDB'; column does not allow nulls. UPDATE fails. 

を、0)が、私はむしろ、ヌルを持っていると思います0の

+0

グローバルな一時表が使用されていることがわかりました。これらは並行性に非常に問題があります。このエラーが発生する理由は、列のテーブル定義がNULLを許可しないためです。 –

+2

この解決策は恐ろしいことです。 'StudentID'と' Month'がプライマリキーを構成する適切に正規化されたテーブルを使うのはなぜですか?毎月テーブルに列を動的に追加し、それを更新するために動的SQLを使用することは、災害のためのレシピです。別のアプローチを採用していますか? – GarethD

+0

別のアプローチを学ぶためにいつも開いています@GarethD 私はそれをうまくやっていましたが、より良い解決のための情報源があれば、お望みなら送信してください。 – AndrewRx

答えて

-1
がにあなたのALTER TABLEを変更

より:私は、正規化された代替手段を提供していませんでしたので、ダウン票であると仮定すると、

EXEC('ALTER TABLE StudentRanking 
    ADD ' + @DateTXT + ' smallint NULL') 

、私はこの種の問題のためPIVOTを使用してお勧めします。

セットアップ:決して複数のレコードごとがあるので

CREATE TABLE dbo.StudentRanking 
(
     MonthID CHAR(3) NOT NULL, 
     StudentID INT NOT NULL, 
     Score INT NOT NULL, 
    CONSTRAINT PK_StudentRanking__StudentID_Date PRIMARY KEY(MonthID, StudentID), 
); 

INSERT INTO dbo.StudentRanking VALUES ('JAN', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('FEB', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('MAR', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('APR', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('MAY', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('JUN', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('JUL', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('AUG', 56321, 3) 
INSERT INTO dbo.StudentRanking VALUES ('SEP', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('OCT', 56321, 3) 
INSERT INTO dbo.StudentRanking VALUES ('NOV', 56321, 2) 
INSERT INTO dbo.StudentRanking VALUES ('DEC', 56321, 2) 

INSERT INTO dbo.StudentRanking VALUES ('JAN', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('FEB', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('MAR', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('APR', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('MAY', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('JUN', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('JUL', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('AUG', 56821, 2) 
INSERT INTO dbo.StudentRanking VALUES ('SEP', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('OCT', 56821, 2) 
INSERT INTO dbo.StudentRanking VALUES ('NOV', 56821, 1) 
INSERT INTO dbo.StudentRanking VALUES ('DEC', 56821, 1) 

INSERT INTO dbo.StudentRanking VALUES ('JAN', 56021, 3) 
INSERT INTO dbo.StudentRanking VALUES ('FEB', 56021, 3) 
INSERT INTO dbo.StudentRanking VALUES ('MAR', 56021, 3) 
INSERT INTO dbo.StudentRanking VALUES ('APR', 56021, 3) 
INSERT INTO dbo.StudentRanking VALUES ('MAY', 56021, 3) 
INSERT INTO dbo.StudentRanking VALUES ('JUN', 56021, 4) 
INSERT INTO dbo.StudentRanking VALUES ('JUL', 56021, 5) 

クエリ

SELECT * FROM StudentRanking 
PIVOT (SUM(Score) FOR MonthID IN (JAN, FEB, MAR, APR, 
MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC)) AS PVT 

結果

Query Output

SUM(SCORE)は、このインスタンスでは無害です月に1人の学生。 PIVOTが何を回避するかを知ることができるだけです。

+0

あなたのソリューションはとてもうまく動作します。ピボットオプションはCASE文を使うよりもエレガントです。両方のソリューションは私のものよりはるかに簡単です。ありがとうございました! – AndrewRx

0

@GarethDとは違う、より良いアプローチで常にオープン!

私は実際にそれが実行して動作するようになった:

EXEC('ALTER TABLE StudentRanking 
ADD ' + @Date_TXT + ' smallint DEFAULT(null)') 

WHEN MATCHED AND TARGET.' + @Date_TXT + ' IS NULL 
THEN UPDATE SET TARGET.' + @Date_TXT + ' = SOURCE.' + @Date_TXT + ' 
0

をクイックフィックスは、列がNULL可能でないようにすることです。しかし、この解決法はうまく拡張されないことを指摘しておく価値があります。よりスケーラブルなアプローチは、StudentIDMonthがプライマリキーを構成する適切に正規化されたテーブルを使用することです。

あなたは、その後のようなものがあります:あなたもインデックスさを作成することができます

CREATE VIEW dbo.StudentRankingByYear 
WITH SCHEMABINDING 
AS 
    SELECT StudentID, 
      Year = DATEPART(YEAR, Date), 
      Jan = SUM(CASE WHEN DATEPART(MONTH, Date) = 1 THEN Score END), 
      Feb = SUM(CASE WHEN DATEPART(MONTH, Date) = 2 THEN Score END), 
      Mar = SUM(CASE WHEN DATEPART(MONTH, Date) = 3 THEN Score END), 
      Apr = SUM(CASE WHEN DATEPART(MONTH, Date) = 4 THEN Score END), 
      May = SUM(CASE WHEN DATEPART(MONTH, Date) = 5 THEN Score END), 
      Jun = SUM(CASE WHEN DATEPART(MONTH, Date) = 6 THEN Score END), 
      Jul = SUM(CASE WHEN DATEPART(MONTH, Date) = 7 THEN Score END), 
      Aug = SUM(CASE WHEN DATEPART(MONTH, Date) = 8 THEN Score END), 
      Sep = SUM(CASE WHEN DATEPART(MONTH, Date) = 9 THEN Score END), 
      Oct = SUM(CASE WHEN DATEPART(MONTH, Date) = 10 THEN Score END), 
      Nov = SUM(CASE WHEN DATEPART(MONTH, Date) = 11 THEN Score END), 
      Dec = SUM(CASE WHEN DATEPART(MONTH, Date) = 12 THEN Score END) 
    FROM dbo.StudentRanking 
    GROUP BY StudentID, DATEPART(YEAR, Date); 
GO 

:あなたは、あなたが望む形式でテーブルを得るために、このの上にビューを作成することができます

CREATE TABLE dbo.StudentRanking 
(
     Date DATE NOT NULL, 
     StudentID INT NOT NULL, 
     Score INT NOT NULL, 
    CONSTRAINT PK_StudentRanking__StudentID_Date PRIMARY KEY(Date, StudentID), 
); 

をあなたが望む形式でテーブルを取得するためにこれを見てください(実際に必要な場合は、ビューが必要なくても十分なパフォーマンスが得られます)。唯一の違いはヌルカラムを持つことができないことです0として表示する必要があります:

CREATE VIEW dbo.StudentRankingByYear 
WITH SCHEMABINDING 
AS 
    SELECT StudentID, 
      Year = DATEPART(YEAR, Date), 
      Jan = SUM(CASE WHEN DATEPART(MONTH, Date) = 1 THEN Score ELSE 0 END), 
      Feb = SUM(CASE WHEN DATEPART(MONTH, Date) = 2 THEN Score ELSE 0 END), 
      Mar = SUM(CASE WHEN DATEPART(MONTH, Date) = 3 THEN Score ELSE 0 END), 
      Apr = SUM(CASE WHEN DATEPART(MONTH, Date) = 4 THEN Score ELSE 0 END), 
      May = SUM(CASE WHEN DATEPART(MONTH, Date) = 5 THEN Score ELSE 0 END), 
      Jun = SUM(CASE WHEN DATEPART(MONTH, Date) = 6 THEN Score ELSE 0 END), 
      Jul = SUM(CASE WHEN DATEPART(MONTH, Date) = 7 THEN Score ELSE 0 END), 
      Aug = SUM(CASE WHEN DATEPART(MONTH, Date) = 8 THEN Score ELSE 0 END), 
      Sep = SUM(CASE WHEN DATEPART(MONTH, Date) = 9 THEN Score ELSE 0 END), 
      Oct = SUM(CASE WHEN DATEPART(MONTH, Date) = 10 THEN Score ELSE 0 END), 
      Nov = SUM(CASE WHEN DATEPART(MONTH, Date) = 11 THEN Score ELSE 0 END), 
      Dec = SUM(CASE WHEN DATEPART(MONTH, Date) = 12 THEN Score ELSE 0 END), 
      Records = COUNT_BIG(*) 
    FROM dbo.StudentRanking 
    GROUP BY StudentID, DATEPART(YEAR, Date); 
GO 
CREATE UNIQUE CLUSTERED INDEX UQ_StudentRankingByYear__StudentID_Year 
    ON dbo.StudentRankingByYear (StudentID, Year); 
+0

GarethDに感謝します。どちらのソリューションもうまくいきます。 nullsとゼロ値を許すオプションを提供してくれてありがとう。ありがとう、トン! – AndrewRx