2

名前と値のペアのデータを格納するテーブルを、SQL Server 2008 でリレーショナル形式に変換したいと考えています。

ソース テーブル

Strings
ID  Type    String
100 1   John
100 2   Milton
101 1   Johny
101 2   Gaddar

対象が必要

Customers
ID  FirstName   LastName
100 John        Milton
101 Johny       Gaddar

私は以下の戦略に従っています。

顧客テーブルに文字列テーブルの ID 値を入力します

INSERT INTO CUSTOMERS SELECT DISTINCT ID FROM Strings

次のものが得られます

Customers
ID  FirstName   LastName
100 NULL        NULL
101 NULL        NULL

ID 列を使用して文字列に結合することにより、残りの属性で Customers を更新します。このように、Customers の各レコードには、対応する 2 つの一致するレコードがあります。

UPDATE Customers
    SET FirstName = (CASE WHEN S.Type=1 THEN S.String ELSE FirstName)
        LastName = (CASE WHEN S.Type=2 THEN S.String ELSE LastName)
FROM Customers
    INNER JOIN Strings ON Customers.ID=Strings.ID

中間状態は似ていますが、

ID  FirstName   LastName    ID  Type    String
100 John        NULL        100 1   John
100 NULL        Milton      100 2   Milton
101 Johny       NULL        101 1   Johny
101 NULL        Gaddar      101 2   Gaddar

しかし、これは期待どおりに機能していません。句で値を割り当てるSETと、コミットされていない値ではなく、コミットされた値のみが設定されるためです。コミットされていない値を (クエリの処理時間内に)UPDATEステートメントに設定する方法はありますか?

PS:代替ソリューションを探しているわけではありませんが、コミットされていないデータをUPDATE.

4

3 に答える 3

1

これを行う最も簡単な方法は、更新を2つに分割することです。

UPDATE Customers
SET FirstName = Strings.String
FROM Customers
INNER JOIN Strings ON Customers.ID=Strings.ID AND Strings.Type = 1

その後:

UPDATE Customers
SET LastName = Strings.String
FROM Customers
INNER JOIN Strings ON Customers.ID=Strings.ID AND Strings.Type = 2

派生テーブルなどの1つのクエリでそれを行う方法はおそらくありますが、それが特定の要件でない限り、私はこのアプローチを使用します。

于 2009-12-08T14:04:29.210 に答える
0

Astanderは正しいです(私は彼の答えを受け入れています)。読み取り UNCOMMITTED の問題が原因で更新が行われていませんが、JOIN によって返された複数の行が原因です。これを確認しました。UPDATE は、複数のレコードから生成された最初の行のみを選択して、元のテーブルを更新します。これは MSSQL、Sybase などの RDMBMS の動作ですが、Oracle ではこの種の更新が許可されておらず、エラーがスローされます。MSSQL でこのことを確認しました。

また、MSSQL は UNCOMMITTED データによるセルの更新をサポートしていません。他のRDBMSでの状況がわからない。また、anyRDBMS が ISOLATION レベルの管理をクエリで提供しているかどうかはわかりません。

別の解決策は、集計してピボットを解除してから挿入するという 2 つの手順で行うことです。これは、上記の回答で与えられた方法と比較してスキャンが少なくなります。

INSERT INTO Customers
SELECT 
    ID
    ,MAX(CASE WHEN Type = 1 THEN String ELSE NULL END) AS FirstName
    ,MAX(CASE WHEN Type = 2 THEN String ELSE NULL END) AS LastName
FROM Strings
GROUP BY ID 

これを手伝ってくれた友人のRoji Thomasに感謝します。

于 2009-12-10T12:08:46.730 に答える
0

これを見てください、それはあなたが持っていたすべてのステップを避けるべきです

DECLARE @Table TABLE(
        ID INT,
        Type INT,
        String VARCHAR(50)
)
INSERT INTO @Table (ID,[Type],String) SELECT 100 ,1   ,'John'
INSERT INTO @Table (ID,[Type],String) SELECT 100 ,2   ,'Milton'
INSERT INTO @Table (ID,[Type],String) SELECT 101 ,1   ,'Johny'
INSERT INTO @Table (ID,[Type],String) SELECT 101 ,2   ,'Gaddar'

SELECT  IDs.ID,
        tName.String NAME,
        tSur.String Surname
FROM    (
            SELECT DISTINCT ID
            FROM @Table
        ) IDs LEFT JOIN
        @Table tName ON IDs.ID = tName.ID AND tName.[Type] = 1  LEFT JOIN
        @Table tSur ON IDs.ID = tSur.ID AND tSur.[Type] = 2

OK、あなたが探しているものに対する解決策が見つかるとは思いません。UPDATE(Transact-SQL)から、それは述べています

FROM 句での UPDATE の使用

UPDATE ステートメントに FROM 節が含まれていて、更新される各列オカレンスに対して 1 つの値しか使用できないように指定されていない場合、つまり UPDATE ステートメントが決定論的でない場合、UPDATE ステートメントの結果は未定義です。たとえば、次のスクリプトの UPDATE ステートメントでは、Table1 の両方の行が UPDATE ステートメントの FROM 句の条件を満たしています。ただし、Table1 のどの行を使用して Table2 の行を更新するかは未定義です。

USE AdventureWorks;
GO
IF OBJECT_ID ('dbo.Table1', 'U') IS NOT NULL
    DROP TABLE dbo.Table1;
GO
IF OBJECT_ID ('dbo.Table2', 'U') IS NOT NULL
    DROP TABLE dbo.Table2;
GO
CREATE TABLE dbo.Table1 
    (ColA int NOT NULL, ColB decimal(10,3) NOT NULL);
GO
CREATE TABLE dbo.Table2 
    (ColA int PRIMARY KEY NOT NULL, ColB decimal(10,3) NOT NULL);
GO
INSERT INTO dbo.Table1 VALUES(1, 10.0), (1, 20.0), (1, 0.0);
GO

UPDATE dbo.Table2 
SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
FROM dbo.Table2 
    INNER JOIN dbo.Table1 
    ON (dbo.Table2.ColA = dbo.Table1.ColA);
GO
SELECT ColA, ColB 
FROM dbo.Table2;
于 2009-12-08T14:11:08.390 に答える