2009-09-28 32 views
42

Làm thế nào để bạn nói như sau trong Microsoft SQL Server 2005:IF EXISTS, sau đó chọn INSERT ELSE và sau đó chọn

IF EXISTS (SELECT * FROM Table WHERE FieldValue='') THEN 
    SELECT TableID FROM Table WHERE FieldValue='' 
ELSE 
    INSERT INTO TABLE(FieldValue) VALUES('') 
    SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY() 
END IF 

Những gì tôi đang cố gắng làm là để xem nếu có một fieldValue trống đã có, và nếu có sau đó trả lại TableID đó, hãy chèn một giá trị trường trống và trả về khóa chính tương ứng.

+1

Bạn đang sử dụng ngôn ngữ lập trình nào? Điều này có thể được thực hiện tốt hơn ở bên trong một giao dịch. –

+0

bản sao có thể có của [Chỉ chèn hàng nếu nó chưa có ở đó] (http://stackoverflow.com/questions/3407857/only-inserting-a-row-if-its-not-already-there) –

+0

Đây là câu hỏi cũ; câu hỏi 'Chỉ chèn một hàng' phải được đóng dưới dạng bản sao của câu hỏi này, chứ không phải theo cách khác. –

Trả lời

6

Bạn đang ở rất gần:

IF EXISTS (SELECT * FROM Table WHERE FieldValue='') 
    SELECT TableID FROM Table WHERE FieldValue='' 
ELSE 
BEGIN 
    INSERT INTO TABLE (FieldValue) VALUES ('') 
    SELECT TableID FROM Table WHERE TableID=SCOPE_IDENTITY() 
END 
28
IF EXISTS (SELECT 1 FROM Table WHERE FieldValue='') 
BEGIN 
    SELECT TableID FROM Table WHERE FieldValue='' 
END 
ELSE 
BEGIN 
    INSERT INTO TABLE(FieldValue) VALUES('') 
    SELECT SCOPE_IDENTITY() AS TableID 
END 

Xem here để biết thêm thông tin về NẾU ELSE

Lưu ý: viết mà không có một SQL Server cài đặt tiện dụng để kiểm tra điều này nhưng tôi nghĩ rằng nó là đúng

tăng gấp đôi

Ngoài ra, tôi đã thay đổi bit EXISTS để thực hiện SELECT 1 chứ không phải SELECT * vì bạn không quan tâm đến những gì được trả về trong một EXISTS, miễn là một cái gì đó là Tôi cũng đã thay đổi chút SCOPE_IDENTITY() để trở về chỉ là danh tính giả định rằng TableID là cột sắc

+2

'CHỌN 1' không quan trọng. Bạn đang thay đổi nó chỉ để chỉ ra rằng bạn không quan tâm đến các chi tiết? Nó không giúp hiệu suất. –

+2

SELCET 1, SELECT NULL, SELECT * đều bằng nhau trong EXISTS ... – gbn

+4

Tôi thích tránh SELECT * trong số mã của tôi - nó không giống như thói quen tốt để vào, vì vậy tôi thường làm 1 CHỌN 1 khi làm một tồn tại – Jane

58

Bạn cần phải làm điều này trong giao dịch để đảm bảo hai khách hàng đồng thời sẽ không chèn cùng fieldValue hai lần:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
BEGIN TRANSACTION 
    DECLARE @id AS INT 
    SELECT @id = tableId FROM table WHERE [email protected] 
    IF @id IS NULL 
    BEGIN 
     INSERT INTO table (fieldValue) VALUES (@newValue) 
     SELECT @id = SCOPE_IDENTITY() 
    END 
    SELECT @id 
COMMIT TRANSACTION 

bạn cũng có thể sử dụng để giảm Double-checked locking khóa overhead

DECLARE @id AS INT 
SELECT @id = tableID FROM table (NOLOCK) WHERE [email protected] 
IF @id IS NULL 
BEGIN 
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
    BEGIN TRANSACTION 
     SELECT @id = tableID FROM table WHERE [email protected] 
     IF @id IS NULL 
     BEGIN 
      INSERT INTO table (fieldValue) VALUES (@newValue) 
      SELECT @id = SCOPE_IDENTITY() 
     END 
    COMMIT TRANSACTION 
END 
SELECT @id 

đối với lý do tại sao cô lập CẤP sERIALIZABLE là cần thiết, khi bạn đang ở trong một giao dịch serializable, tha SELECT đầu tiên t truy cập vào bảng tạo ra một phạm vi khóa bao gồm nơi mà các hồ sơ nên được, vì vậy không ai khác có thể chèn cùng một kỷ lục cho đến khi giao dịch này kết thúc.

Nếu không có mức độ cô lập, mức cô lập mặc định (READ COMMITTED) sẽ không khóa bảng tại thời điểm đọc, vì vậy giữa SELECT và UPDATE, ai đó vẫn có thể chèn. Các giao dịch với mức độ cô lập READ COMMITTED không làm cho SELECT khóa. Giao dịch với READEATABLE READS khóa bản ghi (nếu tìm thấy) nhưng không phải là khoảng trống.

+6

+1 Tôi không hiểu lý do tại sao câu trả lời ** chỉ ** xem xét điều kiện chủng tộc và đồng thời là không hoạt động trên số phiếu bầu 0. –

+2

Có thể bạn mở rộng để giải thích tại sao 'ISOLATION LEVEL SERIALIZABLE' là cần thiết và điều gì có thể xảy ra nếu bạn không thiết lập điều đó? – binki

+5

@binki, khi bên trong một giao dịch có thể tuần tự hóa, lệnh SELECT đầu tiên chạm vào bảng, tạo ra một khóa * phạm vi bao gồm vị trí mà bản ghi nên, vì vậy không ai khác có thể chèn cùng một bản ghi cho đến khi giao dịch này kết thúc. Nếu không có ISOLATION LEVEL SERIALIZABLE, mức cô lập mặc định (READ COMMITTED) sẽ không khóa bảng tại thời điểm đọc, do đó, giữa chọn và cập nhật, ai đó vẫn có thể chèn. Các giao dịch với mức độ cách ly READ COMMITTED, không gây ra SELECT để khóa. Giao dịch với READEATABLE READS, khóa bản ghi (nếu tìm thấy) nhưng không phải là khoảng trống. – zvolkov

2

Bạn chỉ cần phải thay đổi cấu trúc của if...else..endif hơi:

if exists(select * from Table where FieldValue='') then begin 
    select TableID from Table where FieldValue='' 
end else begin 
    insert into Table (FieldValue) values ('') 
    select TableID from Table where TableID = scope_identity() 
end 

Bạn cũng có thể làm:

if not exists(select * from Table where FieldValue='') then begin 
    insert into Table (FieldValue) values ('') 
end 
select TableID from Table where FieldValue='' 

Hoặc:

if exists(select * from Table where FieldValue='') then begin 
    select TableID from Table where FieldValue='' 
end else begin 
    insert into Table (FieldValue) values ('') 
    select scope_identity() as TableID 
end 
1
DECLARE @t1 TABLE (
    TableID  int   IDENTITY, 
    FieldValue varchar(20) 
) 

--<< No empty string 
IF EXISTS (
    SELECT * 
    FROM @t1 
    WHERE FieldValue = '' 
) BEGIN 
    SELECT TableID 
    FROM @t1 
    WHERE FieldValue='' 
END 
ELSE BEGIN 
    INSERT INTO @t1 (FieldValue) VALUES ('') 
    SELECT SCOPE_IDENTITY() AS TableID 
END 

--<< A record with an empty string already exists 
IF EXISTS (
    SELECT * 
    FROM @t1 
    WHERE FieldValue = '' 
) BEGIN 
    SELECT TableID 
    FROM @t1 
    WHERE FieldValue='' 
END 
ELSE BEGIN 
    INSERT INTO @t1 (FieldValue) VALUES ('') 
    SELECT SCOPE_IDENTITY() AS TableID 
END 
2

Nghe có vẻ như bàn của bạn không có khóa. Bạn có thể chỉ cần thử số INSERT: nếu đó là bản sao thì ràng buộc khóa sẽ bị lỗi và INSERT sẽ không thành công. Đừng lo: bạn chỉ cần đảm bảo ứng dụng không nhìn thấy/bỏ qua lỗi. Khi bạn nói 'khóa chính', có lẽ bạn có nghĩa là giá trị IDENTITY. Đó là tất cả rất tốt nhưng bạn cũng cần một ràng buộc quan trọng (ví dụ: UNIQUE) trên khóa tự nhiên của bạn.

Ngoài ra, tôi tự hỏi liệu quy trình của bạn có thực hiện quá nhiều hay không. Xem xét có các thủ tục riêng biệt cho hành động 'tạo' và 'đọc' tương ứng.

1
create schema tableName authorization dbo 
go 
IF OBJECT_ID ('tableName.put_fieldValue', 'P') IS NOT NULL 
drop proc tableName.put_fieldValue 
go 
create proc tableName.put_fieldValue(@fieldValue int) as 
declare @tableid int = 0 
select @tableid = tableid from table where fieldValue='' 
if @tableid = 0 begin 
    insert into table(fieldValue) values('') 
    select @tableid = scope_identity() 
end 
return @tableid 
go 
declare @tablid int = 0 
exec @tableid = tableName.put_fieldValue('') 
Các vấn đề liên quan