কোনও সারি বিদ্যমান কিনা তা পরীক্ষা করুন, অন্যথায় .োকান


237

আমার একটি টি-এসকিউএল সঞ্চিত পদ্ধতি লিখতে হবে যা একটি সারণীতে একটি সারি আপডেট করে। যদি সারিটি বিদ্যমান না থাকে তবে এটি sertোকান। এই সমস্ত পদক্ষেপ একটি লেনদেন দ্বারা আবৃত।

এটি একটি বুকিং সিস্টেমের জন্য, সুতরাং এটি অবশ্যই পারমাণবিক এবং নির্ভরযোগ্য হবে । লেনদেন প্রতিশ্রুতিবদ্ধ ছিল এবং ফ্লাইট বুক করা থাকলে এটি অবশ্যই সত্য প্রত্যাবর্তন করতে হবে।

আমি টি এসকিউএল-এ নতুন এবং কীভাবে ব্যবহার করব সে সম্পর্কে নিশ্চিত নই @@rowcount। এটি আমি এখনও অবধি লিখেছি। আমি কি সঠিক রাস্তায় আছি? আমি নিশ্চিত আপনার পক্ষে সহজ সমস্যা is

-- BEGIN TRANSACTION (HOW TO DO?)

UPDATE Bookings
 SET TicketsBooked = TicketsBooked + @TicketsToBook
 WHERE FlightId = @Id AND TicketsMax < (TicketsBooked + @TicketsToBook)

-- Here I need to insert only if the row doesn't exists.
-- If the row exists but the condition TicketsMax is violated, I must not insert 
-- the row and return FALSE

IF @@ROWCOUNT = 0 
BEGIN

 INSERT INTO Bookings ... (omitted)

END

-- END TRANSACTION (HOW TO DO?)

-- Return TRUE (How to do?)


সম্পর্কিত প্রশ্ন - স্ট্যাকওভারফ্লো.com
বাষ্প

উত্তর:


158

MERGE কমান্ডটি একবার দেখুন । আপনি করতে পারেন UPDATE, INSERTএবং DELETEএকটি বিবৃতিতে।

এখানে ব্যবহারের একটি কার্যকরী বাস্তবায়ন MERGE
- এটি কোনও আপডেট করার আগে ফ্লাইটটি পূর্ণ কিনা তা পরীক্ষা করে।

if exists(select 1 from INFORMATION_SCHEMA.TABLES T 
              where T.TABLE_NAME = 'Bookings') 
begin
    drop table Bookings
end
GO

create table Bookings(
  FlightID    int identity(1, 1) primary key,
  TicketsMax    int not null,
  TicketsBooked int not null
)
GO

insert  Bookings(TicketsMax, TicketsBooked) select 1, 0
insert  Bookings(TicketsMax, TicketsBooked) select 2, 2
insert  Bookings(TicketsMax, TicketsBooked) select 3, 1
GO

select * from Bookings

এবং তারপর ...

declare @FlightID int = 1
declare @TicketsToBook int = 2

--; This should add a new record
merge Bookings as T
using (select @FlightID as FlightID, @TicketsToBook as TicketsToBook) as S
    on  T.FlightID = S.FlightID
      and T.TicketsMax > (T.TicketsBooked + S.TicketsToBook)
  when matched then
    update set T.TicketsBooked = T.TicketsBooked + S.TicketsToBook
  when not matched then
    insert (TicketsMax, TicketsBooked) 
    values(S.TicketsToBook, S.TicketsToBook);

select * from Bookings

6
এছাড়াও, দেখুন কেন আপনি এই মার্জটির সাথে (হোল্ডলক) পছন্দ করতে পারেন ।
ইউজিন রায়বতসেভ

4
আমি মনে করি মার্জ 2005 এর পরে সমর্থিত (তাই ২০০+)।
সামিস

3
উইথ উইথ মার্জ (আপলোডক) প্রাথমিক কী লঙ্ঘন হতে পারে, যা এই ক্ষেত্রে খারাপ হবে be (দেখুন [একত্রীকরণ SQL2008 মধ্যে একটি পারমাণবিক বক্তব্য কি?] Stackoverflow.com/questions/9871644/... )
জেমস

156

আমি প্রতিটি ফ্লাইটের জন্য একটি একক সারি ধরে নিই? যদি তাই:

IF EXISTS (SELECT * FROM Bookings WHERE FLightID = @Id)
BEGIN
    --UPDATE HERE
END
ELSE
BEGIN
   -- INSERT HERE
END

আমি যা বলেছিলাম তা ধরে নিয়েছি, আপনার কাজ করার পদ্ধতিটি কোনও ফ্লাইটকে ওভারবুক করতে পারে, কারণ এটিতে একটি নতুন সারি সন্নিবেশ করা হবে যখন 10 টি টিকিট সর্বাধিক থাকবে এবং আপনি 20 বুকিং দিচ্ছেন।


হ্যাঁ. প্রতি ফ্লাইটে 1 সারি রয়েছে। তবে আপনার কোডটি নির্বাচন করুন তবে আপডেটের আগে ফ্লাইটটি পূর্ণ কিনা তা পরীক্ষা করে না। কিভাবে এই কাজ করতে?

2
জাতিগত অবস্থার কারণে এটি কেবলমাত্র সঠিক যদি বর্তমান লেনদেনের বিচ্ছিন্নতা স্তরটি সিরিয়ালাইজেবল হয়।
জেরেক প্রাইজিডজকি

1
@ মার্টিন: উত্তরটি হাতে থাকা প্রশ্নের দিকেই নিবদ্ধ ছিল। ওপির নিজস্ব বিবৃতি থেকে "একটি লেনদেনের দ্বারা মোড়ানো এই সমস্ত পদক্ষেপ"। যদি লেনদেনটি সঠিকভাবে প্রয়োগ করা হয় তবে থ্রেড নিরাপদ ইস্যু হওয়া উচিত নয়।
গ্রেগরি এ বিমার

14
@ গ্রেগরিএবিমার - কেবলমাত্র এটি কোনও BEGIN TRAN ... COMMITডিফল্ট বিচ্ছিন্ন স্তরে চাপলে সমস্যা সমাধান হবে না। ওপি নির্দিষ্ট করে যে পারমাণবিক এবং নির্ভরযোগ্য প্রয়োজনীয়তা। আপনার উত্তরটি কোনও আকার বা আকারে এটি সম্বোধন করতে ব্যর্থ।
মার্টিন স্মিথ

2
যদি (ইউপডলক, হোল্ডলোক) নির্বাচনটিতে যুক্ত করা হয় তবে এটি কি থ্রেড-নিরাপদ হবে IF EXISTS (SELECT * FROM Bookings (UPDLOCK, HOLDLOCK) WHERE FLightID = @Id)?
জিম

67

সারিটির অস্তিত্বের পরীক্ষা করার সময় আপডেটলক, রোলক, হোল্ডলক ইঙ্গিতগুলি পাস করুন।

begin tran /* default read committed isolation level is fine */

if not exists (select * from Table with (updlock, rowlock, holdlock) where ...)
    /* insert */
else
    /* update */

commit /* locks are released here */

আপডেটলোক ইঙ্গিতটি ক্যোয়ারিকে সারিটিতে আপডেট লক নিতে বাধ্য করে, যদি আপনি প্রতিশ্রুতিবদ্ধ না হন বা ফিরে না ফেরা পর্যন্ত অন্যান্য লেনদেনগুলিকে এটি পরিবর্তন করতে বাধা দেয়।

হোল্ডলক ইঙ্গিতটি ক্যোয়ারিকে একটি রেঞ্জ লক নিতে বাধ্য করে, অন্য লেনদেনগুলিকে আপনার ফিল্টার মানদণ্ডের সাথে মিলিয়ে একটি সারি যোগ করা থেকে বিরত রাখে যতক্ষণ না আপনি অঙ্গীকারবদ্ধ হন বা ফিরে না চলে।

রোউলক ইঙ্গিতটি ডিফল্ট পৃষ্ঠার স্তরের পরিবর্তে সারি স্তরে লক গ্রানুলারিটিকে বাধ্য করে, সুতরাং আপনার লেনদেন একই পাতায় সম্পর্কযুক্ত সারিগুলি আপডেট করার চেষ্টা করা অন্যান্য লেনদেনকে অবরুদ্ধ করবে না (তবে হ্রাসযুক্ত বিতর্ক এবং বৃদ্ধি বৃদ্ধির মধ্যে বাণিজ্য বন্ধ সম্পর্কে সচেতন থাকুন ওভারহেড লক করা - আপনার একক লেনদেনে প্রচুর পরিমাণে সারি-স্তরের লক নেওয়া এড়ানো উচিত)।

আরও তথ্যের জন্য http://msdn.microsoft.com/en-us/library/ms187373.aspx দেখুন ।

নোট করুন যে লকগুলি যেগুলি গ্রহণ করে সেগুলি কার্যকর করা হিসাবে গ্রহণ করা হয় - শুরু করা ত্রান আপনাকে অন্য কোনও লেনদেনের বিরুদ্ধে লক পিন করার আগে কোনওরকম প্রতিরোধের প্রতিরোধ ক্ষমতা দেয় না যা আপনার কাছে পৌঁছানোর আগে। যত তাড়াতাড়ি সম্ভব লেনদেনের প্রতিশ্রুতি দিয়ে (দেরী অর্জন, তাড়াতাড়ি মুক্তি দিন) কমপক্ষে স্বল্পতম সময়ের জন্য লক ধরে রাখতে আপনার এসকিউএলকে ফ্যাক্ট করা উচিত।

মনে রাখবেন যে আপনার পিকে বিগিন্ট হলে সারি-স্তরের লকগুলি কম কার্যকর হতে পারে, যেহেতু এসকিউএল সার্ভারে অভ্যন্তরীণ হ্যাশিং 64৪-বিট মানগুলির জন্য অবনমিত হয় (বিভিন্ন কী মান একই লক আইডিতে হ্যাশ করতে পারে)।


4
ওভারবুকিং এড়াতে লক করা খুব গুরুত্বপূর্ণ। আইএফ স্টেটমেন্টে ঘোষিত একটি লক আইএফ স্টেটমেন্টের শেষ হওয়া পর্যন্ত, অর্থাৎ একটি আপডেট স্টেটমেন্টের জন্য ধরে রাখা ঠিক কি ধরে নেওয়া যায়? তারপরে নতুন কোডগুলি আপনার কোডটি অনুলিপি করা এবং আপনার কোড আটকে দেওয়া এবং এখনও এটি ভুল হওয়া থেকে বিরত রাখতে উপরের কোডটি শুরুর দিকে ব্লক চিহ্নগুলি ব্যবহার করে বুদ্ধিমানের কাজ হতে পারে।
সাইমন বি।

আমার পিকে যদি বার্চার হয় (তবে সর্বাধিক নয়) বা তিনটি VARCHAR কলামের সংমিশ্রণে সমস্যা আছে?
বাষ্প

আমি এই উত্তরটির সাথে সম্পর্কিত একটি প্রশ্ন তৈরি করেছি - stackoverflow.com/questions/21945850/… প্রশ্নটি এই কোডটি কয়েক মিলিয়ন সারি forোকানোর জন্য ব্যবহার করা যেতে পারে?
বাষ্প

যখন অনেক থ্রেড প্রায়শই ইতিমধ্যে বিদ্যমান সারিগুলি পরীক্ষা করে তখন এই সমাধানটি খুব বেশি লকিং ওভারহেড চাপিয়ে দেয়। আমার ধারণা, existsলকিং ইঙ্গিত ছাড়াই প্রতিরোধমূলক অতিরিক্ত চেকের মাধ্যমে এক ধরণের ডাবল চেকড লকিংয়ের সাথে কাজ করা যেতে পারে ।
ভাদজিম

38

আমি আমার সমাধান লিখছি। আমার পদ্ধতি 'যদি' বা 'একত্রিত' হয় না। আমার পদ্ধতি সহজ।

INSERT INTO TableName (col1,col2)
SELECT @par1, @par2
   WHERE NOT EXISTS (SELECT col1,col2 FROM TableName
                     WHERE col1=@par1 AND col2=@par2)

উদাহরণ স্বরূপ:

INSERT INTO Members (username)
SELECT 'Cem'
   WHERE NOT EXISTS (SELECT username FROM Members
                     WHERE username='Cem')

ব্যাখ্যা:

(1) সারণী নাম থেকে কোল 1 নির্বাচন করুন, কোল 1 = @ par1 এবং কল 2 = @ par2 এটি টেবিল নাম অনুসন্ধান করা মান থেকে নির্বাচন করে

(২) @ par1, @ par2 নির্বাচন করুন যেখানে অস্তিত্ব নেই (1) উপশক্তি থেকে উপস্থিত না থাকলে এটি লাগে

(3) টেবিলনাম (2) পদক্ষেপের মানগুলিতে সন্নিবেশ করান


1
এটি কেবল সন্নিবেশ করার জন্য, আপডেট করার জন্য নয়।
সেমে


3

নিম্নলিখিত মডেলটি ব্যবহার করে আমি অবশেষে একটি সারি সন্নিবেশ করতে সক্ষম হয়েছি যে এটি ইতিমধ্যে বিদ্যমান ছিল না:

INSERT INTO table ( column1, column2, column3 )
(
    SELECT $column1, $column2, $column3
      WHERE NOT EXISTS (
        SELECT 1
          FROM table 
          WHERE column1 = $column1
          AND column2 = $column2
          AND column3 = $column3 
    )
)

যা আমি এখানে পেয়েছি:

http://www.postgresql.org/message-id/87hdow4ld1.fsf@stark.xeocode.com


1
এটি কেবল একটি অনুলিপি-পেস্ট লিঙ্ক ... মন্তব্য হিসাবে ভাল উপযুক্ত।
আয়ান

2

এই জিনিসটি আমি সম্প্রতি করতে হয়েছিল:

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[cjso_UpdateCustomerLogin]
    (
      @CustomerID AS INT,
      @UserName AS VARCHAR(25),
      @Password AS BINARY(16)
    )
AS 
    BEGIN
        IF ISNULL((SELECT CustomerID FROM tblOnline_CustomerAccount WHERE CustomerID = @CustomerID), 0) = 0
        BEGIN
            INSERT INTO [tblOnline_CustomerAccount] (
                [CustomerID],
                [UserName],
                [Password],
                [LastLogin]
            ) VALUES ( 
                /* CustomerID - int */ @CustomerID,
                /* UserName - varchar(25) */ @UserName,
                /* Password - binary(16) */ @Password,
                /* LastLogin - datetime */ NULL ) 
        END
        ELSE
        BEGIN
            UPDATE  [tblOnline_CustomerAccount]
            SET     UserName = @UserName,
                    Password = @Password
            WHERE   CustomerID = @CustomerID    
        END

    END

1

আপনি অর্জনের জন্য মার্জ কার্যকারিতাটি ব্যবহার করতে পারেন । অন্যথায় আপনি এটি করতে পারেন:

declare @rowCount int

select @rowCount=@@RowCount

if @rowCount=0
begin
--insert....

0

সম্পূর্ণ সমাধান নীচে (কার্সার কাঠামো সহ)। begin trans ... commitউপরের পোস্ট থেকে কোডটির জন্য ক্যাসিয়াস পর্কাসকে অনেক ধন্যবাদ ।

declare @mystat6 bigint
declare @mystat6p varchar(50)
declare @mystat6b bigint

DECLARE mycur1 CURSOR for

 select result1,picture,bittot from  all_Tempnogos2results11

 OPEN mycur1

 FETCH NEXT FROM mycur1 INTO @mystat6, @mystat6p , @mystat6b

 WHILE @@Fetch_Status = 0
 BEGIN

 begin tran /* default read committed isolation level is fine */

 if not exists (select * from all_Tempnogos2results11_uniq with (updlock, rowlock, holdlock)
                     where all_Tempnogos2results11_uniq.result1 = @mystat6 
                        and all_Tempnogos2results11_uniq.bittot = @mystat6b )
     insert all_Tempnogos2results11_uniq values (@mystat6 , @mystat6p , @mystat6b)

 --else
 --  /* update */

 commit /* locks are released here */

 FETCH NEXT FROM mycur1 INTO @mystat6 , @mystat6p , @mystat6b

 END

 CLOSE mycur1

 DEALLOCATE mycur1
 go

0
INSERT INTO [DatabaseName1].dbo.[TableName1] SELECT * FROM [DatabaseName2].dbo.[TableName2]
 WHERE [YourPK] not in (select [YourPK] from [DatabaseName1].dbo.[TableName1])

-2
INSERT INTO table ( column1, column2, column3 )
SELECT $column1, $column2, $column3
EXCEPT SELECT column1, column2, column3
FROM table

সারণির ভিতরে সারণি (কলাম 1, কলাম 2, কলাম 3) নির্বাচন করুন $ কলাম 1, $ কলাম 2, $ কলাম 3 ছাড় করুন নির্বাচন করুন কলাম 1, কলাম 2, কলাম3 টেবিল থেকে
অ্যারন

1
এই প্রশ্নের অনেক উচ্চ upvated উত্তর আছে। এই উত্তরটি বিদ্যমান উত্তরগুলিতে কী যুক্ত করে তা ব্যাখ্যা করার জন্য আপনি কী ব্যাখ্যা করতে পারেন?
ফ্রেঞ্চিস

-2

এই সমস্যার সর্বোত্তম পন্থাটি হ'ল প্রথমে ডাটাবেস কলামটি অনন্য করা

ALTER TABLE table_name ADD UNIQUE KEY

THEN INSERT IGNORE INTO table_name , মানটি সন্নিবেশ করা হবে না যদি এটির একটি সদৃশ কী / ফলাফল ইতিমধ্যে সারণীতে উপস্থিত থাকে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.