আমি কীভাবে কোনও রেকর্ডকে বলিয়ান কলামের সত্যিকারের মান এবং অন্য সকলকে একটি ভুয়া মান রাখতে বাধ্য করি?


20

আমি প্রয়োগ করতে চাই যে কোনও সারণীতে কেবলমাত্র একটি রেকর্ডকে অন্য প্রশ্ন বা ভিউগুলির জন্য "ডিফল্ট" মান হিসাবে বিবেচনা করা হয় যা সেই টেবিলটিতে অ্যাক্সেস করতে পারে।

মূলত, আমি গ্যারান্টি দিতে চাই যে এই কোয়েরিটি সর্বদা ঠিক এক সারিতে ফিরে আসবে:

SELECT ID, Zip 
FROM PostalCodes 
WHERE isDefault=True

এসকিউএল এ আমি কীভাবে করব?


1
"আমি গ্যারান্টি দিতে চাই যে এই কোয়েরিটি সর্বদা ঠিক এক সারিতে ফিরে আসবে" - সর্বদা? PostalCodesখালি কখন কী হবে ? যদি একটি সারিটির ইতিমধ্যে একটি সম্পত্তি থাকা উচিত, অন্য কোনও সারি (যদি এটি উপস্থিত থাকে) একই এসকিউএল বিবৃতিতে সত্য হিসাবে সেট না করা হয় তবে এটি মিথ্যাতে সেট হওয়া থেকে বিরত থাকবে? শূন্য সারিতে লেনদেনের সীমানার মধ্যে সম্পত্তি থাকতে পারে? সারণির শেষ সারিটি সম্পত্তি রাখতে বাধ্য হওয়া উচিত এবং মুছে ফেলা থেকে বাধা দেওয়া উচিত? অভিজ্ঞতা আমাকে বলেছে যে "সঠিকভাবে এক সারির গ্যারান্টি" হ'ল বাস্তবে অন্যরকম কিছু অর্থ বোঝায়, প্রায়শই কেবল "সর্বাধিক এক সারি"।
onedaywhen

উত্তর:


8

সম্পাদনা করুন: আমরা মাইএসকিউএল জানার আগে এটি এসকিউএল সার্ভারের সাথে জড়িত

এটি করার 4 টি উপায় রয়েছে: সর্বাধিক কাঙ্ক্ষিত থেকে কমপক্ষে কাঙ্ক্ষিত ক্রম

আমরা জানি না এটি কী আরডিবিএমএস তবে যদি না হয় তবে সমস্ত প্রযোজ্য নাও হতে পারে

  1. সবচেয়ে ভাল উপায় হ'ল একটি পরিশোধিত সূচক। এটি স্বতন্ত্রতা বজায় রাখতে ডিআরআই ব্যবহার করে।

  2. স্বতন্ত্রতার সাথে গণিত কলাম (জ্যাক ডগলাসের উত্তর দেখুন) (সম্পাদনা 2 দ্বারা যুক্ত)

  3. একটি সূচকযুক্ত / উপাদানযুক্ত দর্শন যা ডিআরআই ব্যবহার করে ফিল্টার করা সূচির মতো

  4. ট্রিগার (অন্যান্য উত্তর অনুসারে)

  5. একটি ইউডিএফ দিয়ে সীমাবদ্ধতা পরীক্ষা করুন। এটি সম্মতি এবং স্ন্যাপশট বিচ্ছিন্নতার জন্য নিরাপদ নয় । দেখুন ওয়ান দুই তিন চার

মনে রাখবেন যে "এক ডিফল্ট" এর প্রয়োজনীয়তা টেবিলে রয়েছে, সঞ্চিত পদ্ধতি নয়। সঞ্চিত পদ্ধতিতে ইউডিএফের সাথে চেক সীমাবদ্ধতার মতো একই সামঞ্জস্য সমস্যা থাকবে

দ্রষ্টব্য: অনেক বার জিজ্ঞাসা করা হয়েছে:


gbn - সূচক ফিল্টার মত দেখাচ্ছে / ডিআরআই সমাধান শুধুমাত্র সৌন্দর্য সুতরাং এসকিউএল 2008 মত আমি বিকল্প 2. চেষ্টা এড়িয়ে যাবে
emaynard

10

দ্রষ্টব্য: এই উত্তরটি স্পষ্ট হওয়ার আগেই দেওয়া হয়েছিল প্রশ্নকর্তা মাইএসকিউএল নির্দিষ্ট কিছু চাইছিলেন। এই উত্তরটি এসকিউএল সার্ভারের পক্ষপাতদুষ্ট।

সারণিতে একটি চেক সীমাবদ্ধতা প্রয়োগ করুন যা একটি ইউডিএফ কল করে এবং এটির ফিরে আসার মানটি <= 1 নিশ্চিত করে যে ইউডিএফ কেবল সারণীতে সারিগুলি কোথায় গণনা করতে পারে isDefault = TRUE। এটি নিশ্চিত করবে যে সারণীতে 1 টির বেশি ডিফল্ট সারি নেই।

আপনি একটি যোগ করা উচিত বিটম্যাপ বা ফিল্টার উপর সূচক isDefaultনিশ্চিত এই ইউডিএফ খুব দ্রুত রান করতে কলাম (আপনার platforom উপর নির্ভর করে)।

আদেশ সহকারে

  • চেক সীমাবদ্ধতার নির্দিষ্ট সীমাবদ্ধতা রয়েছে । যথা, এগুলি সারি পরিবর্তিত প্রতিটি সারির জন্য আহ্বান জানানো হয়েছে, এমনকি যদি সেই সারিগুলি কোনও লেনদেনে এখনও প্রতিশ্রুতিবদ্ধ না হয়। সুতরাং আপনি যদি কোনও লেনদেন শুরু করেন, ডিফল্ট হতে একটি নতুন সারি সেট করুন এবং তারপরে পুরানো সারিটি আনসেট করুন, আপনার চেক সীমাবদ্ধতার অভিযোগ করবে। কারণ এটি নতুন সারিটিকে ডিফল্ট করার পরে এটি কার্যকর করা হবে, এখন দুটি ডিফল্ট রয়েছে এবং ব্যর্থ হন। এর পরে সমাধানটি হ'ল এটি নিশ্চিত করা হয় যে আপনি কোনও লেনদেনের ক্ষেত্রে এই কাজটি চালিয়ে যাওয়ার পরেও নতুন ডিফল্ট সেট করার আগে আপনি প্রথমে ডিফল্ট সারিটি আনসেট না করে।
  • জিবিএন যেমন উল্লেখ করেছে , আপনি যদি এসকিউএল সার্ভারে স্ন্যাপশট বিচ্ছিন্নতা ব্যবহার করছেন তবে ইউডিএফগুলি বেমানান ফলাফলগুলি ফিরিয়ে দিতে পারে। যাইহোক, একটি ইনলাইন ইউডিএফ এই সমস্যাটির সাথে চালিত হয় না এবং যে কোনও ইউডিএফ যে কেবল একটি গণনা করে তা ইনলাইন-সক্ষম, এটি এখানে কোনও সমস্যা নয়।
  • ডাটাবেস ইঞ্জিনের প্রসেসিং পাওয়ারের শক্তিটি একবারে ডেটা সেটগুলিতে কাজ করার দক্ষতায় থাকে। একটি ইউডিএফ-ব্যাকড চেক সীমাবদ্ধতার সাথে ইঞ্জিনটি এই ইউডিএফটিকে ক্রমিক পরিবর্তিত প্রতিটি সারিতে ক্রমিকভাবে প্রয়োগ করতে সীমাবদ্ধ থাকবে। আপনার ব্যবহারের ক্ষেত্রে আপনি PostalCodesটেবিলটিতে প্রচুর আপডেট করবেন না এমন সম্ভাবনা কম , তবে সেট-বেসড ক্রিয়াকলাপটি সম্ভবতঃ এমন পরিস্থিতিতেও এটি পারফরম্যান্সের উদ্বেগ হিসাবে রয়ে গেছে।

এই সমস্ত সাবধানবাণী দেওয়া, আমি ব্যবহার করার পরামর্শ দিচ্ছি সাবধানবাণী একটি চেক সীমাবদ্ধতার পরিবর্তে ফিল্টার অনন্য সূচকের জন্য gbn এর পরামর্শ পরামর্শ দিচ্ছি


4

এখানে একটি সঞ্চিত পদ্ধতি (মাইএসকিউএল ডায়ালেক্ট) রয়েছে:

DELIMITER $$
DROP PROCEDURE IF EXISTS SetDefaultForZip;
CREATE PROCEDURE SetDefaultForZip (NEWID INT)
BEGIN
    DECLARE FOUND_TRUE,OLDID INT;

    SELECT COUNT(1) INTO FOUND_TRUE FROM PostalCode WHERE isDefault = TRUE;
    IF FOUND_TRUE = 1 THEN
        SELECT ID INTO OLDID FROM PostalCode WHERE isDefault = TRUE;
        IF NEWID <> OLDID THEN
            UPDATE PostalCode SET isDefault = FALSE WHERE ID = OLDID;
            UPDATE PostalCode SET isDefault = TRUE  WHERE ID = NEWID;
        END IF;
    ELSE
        UPDATE PostalCode SET isDefault = TRUE WHERE ID = NEWID;
    END IF;
END;
$$
DELIMITER ;

আপনার টেবিলটি পরিষ্কার আছে এবং সঞ্চিত পদ্ধতিটি কাজ করছে তা নিশ্চিত করার জন্য, আইডি 200 ডিফল্ট হিসাবে ধরে নিয়ে এই পদক্ষেপগুলি চালান:

ALTER TABLE PostalCode DROP INDEX isDefault_ndx;
UPDATE PostalCodes SET isDefault = FALSE;
ALTER TABLE PostalCode ADD INDEX isDefault_ndx (isDefault);
CALL SetDefaultForZip(200);
SELECT ID FROM PostalCodes WHERE isDefault = TRUE;

একটি সঞ্চিত পদ্ধতি পরিবর্তে একটি ট্রিগার সম্পর্কে কীভাবে?

DELIMITER $$
CREATE TRIGGER postalcodes_bu BEFORE UPDATE ON PostalCodes FOR EACH ROW
BEGIN
    DECLARE FOUND_TRUE,OLDID INT;
    IF NEW.isDefault = TRUE THEN
        SELECT COUNT(1) INTO FOUND_TRUE FROM PostalCode WHERE isDefault = TRUE;
        IF FOUND_TRUE = 1 THEN
            SELECT ID INTO OLDID FROM PostalCode WHERE isDefault = TRUE;
            UPDATE PostalCodes SET isDefault = FALSE WHERE ID = OLDID;
        END IF;
    END IF;
END;
$$
DELIMITER ;

আপনার টেবিলটি পরিষ্কার এবং ট্রিগার কাজ করছে তা নিশ্চিত করার জন্য, আইডি 200 ডিফল্ট হিসাবে ধরে নিয়ে এই পদক্ষেপগুলি চালান:

DROP TRIGGER postalcodes_bu;
ALTER TABLE PostalCode DROP INDEX isDefault_ndx;
UPDATE PostalCodes SET isDefault = FALSE;
ALTER TABLE PostalCode ADD INDEX isDefault_ndx (isDefault);
DELIMITER $$
CREATE TRIGGER postalcodes_bu BEFORE UPDATE ON PostalCodes FOR EACH ROW
BEGIN
    DECLARE FOUND_TRUE,OLDID INT;
    IF NEW.isDefault = TRUE THEN
        SELECT COUNT(1) INTO FOUND_TRUE FROM PostalCode WHERE isDefault = TRUE;
        IF FOUND_TRUE = 1 THEN
            SELECT ID INTO OLDID FROM PostalCode WHERE isDefault = TRUE;
            UPDATE PostalCodes SET isDefault = FALSE WHERE ID = OLDID;
        END IF;
    END IF;
END;
$$
DELIMITER ;
UPDATE PostalCodes SET isDefault = TRUE WHERE ID = 200;
SELECT ID FROM PostalCodes WHERE isDefault = TRUE;

3

বিধি প্রয়োগ করতে আপনি ট্রিগার ব্যবহার করতে পারেন। যখন কোনও আপডেট বা INSERT বিবৃতি সেট হয় যখন ডিফল্ট সত্য হয় তখন ট্রিগারটিতে থাকা এসকিউএল অন্য সমস্ত সারিগুলি মিথ্যাতে সেট করতে পারে।

এটি প্রয়োগ করার সময় আপনাকে অন্যান্য পরিস্থিতি বিবেচনা করতে হবে। উদাহরণস্বরূপ, যদি একের বেশি সারিতে এবং আপডেট করা হয় বা INSERT সেট করা হয় ডিফল্ট ট্রুতে? নিয়ম ভঙ্গ না করার জন্য কোন বিধি প্রয়োগ করা হবে?

এছাড়াও, কোনও শর্ত নেই যেখানে কোনও ডিফল্ট নেই? যখন কোনও আপডেট আইসফায়াল্ট সেট করে সত্য থেকে মিথ্যা সেট করে তখন কী হবে।

একবার আপনি নিয়মগুলি সংজ্ঞায়িত করার পরে, আপনি এগুলি ট্রিগারটিতে তৈরি করতে পারেন।

তারপরে, অন্য উত্তরে বর্ণিত হিসাবে, আপনি বিধি প্রয়োগ করতে সহায়তা করতে একটি চেক সীমাবদ্ধতা প্রয়োগ করতে চান।


3

নিম্নলিখিতটি একটি উদাহরণ সারণী এবং ডেটা সেট আপ করে:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND type in (N'U'))
DROP TABLE [dbo].[MyTable]
GO

CREATE TABLE dbo.MyTable
(
    [id] INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , [IsDefault] BIT DEFAULT 0
)
GO

INSERT dbo.MyTable DEFAULT VALUES
GO 100

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

DECLARE @Id INT
SET @Id = 10

UPDATE
    dbo.MyTable
SET
    IsDefault = CASE WHEN [id] = @Id THEN 1 ELSE 0 END 
GO

টেবিলের আকারের উপর নির্ভর করে, ইসডেফল্ট (ডিইএসসি) এর সূচির ফলে নীচে এক বা উভয় প্রশ্নের সম্পূর্ণ স্ক্যান এড়ানো হতে পারে।

DECLARE @Id INT
SET @Id = 10

UPDATE
    dbo.MyTable
SET
    IsDefault = CASE WHEN [id] = @Id THEN 1 ELSE 0 END
FROM
    dbo.MyTable
WHERE
    [id] = @Id
OR  IsDefault = 1

UPDATE
    dbo.MyTable
SET
    IsDefault = CASE WHEN [id] = @Id THEN 1 ELSE 0 END
FROM
    dbo.MyTable
WHERE
    [id] = @Id
OR  ([id] != @Id AND IsDefault = 1)

আপনি যদি বেস টেবিল থেকে অনুমতিগুলি অপসারণ করতে না পারেন, অন্য উপায়ে সততা প্রয়োগ করতে হবে এবং এসকিউএল ২০০৮ ব্যবহার করছেন, আপনি ফিল্টার করা অনন্য সূচকটি ব্যবহার করতে পারেন:

CREATE UNIQUE INDEX IX_MyTable_IsDefault  ON dbo.MyTable (IsDefault) WHERE IsDefault = 1

1

মাইএসকিউএল-তে ফিল্টার সূচকগুলি নেই, আপনি নীচের মতো কিছু করতে পারেন। এটি MySQL অনন্য সূচকগুলিতে একাধিক NULL গুলি মঞ্জুরি দেয় এবং এই ডেটাটাইপকে অনুকরণ করতে একটি উত্সর্গীকৃত টেবিল এবং একটি বিদেশী কী ব্যবহার করে যা কেবলমাত্র NULL এবং 1 মান হিসাবে রাখতে পারে fact

এই সমাধানটি দিয়ে সত্য / মিথ্যা পরিবর্তে আপনি কেবল 1 / NULL ব্যবহার করবেন।

CREATE TABLE DefaultFlag (id TINYINT UNSIGNED NOT NULL PRIMARY KEY);
INSERT INTO DefaultFlag (id) VALUES (1);

CREATE TABLE PostalCodes (
    ID INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    Zip VARCHAR (32) NOT NULL,
    isDefault TINYINT UNSIGNED NULL DEFAULT NULL,
    CONSTRAINT FOREIGN KEY (isDefault) REFERENCES DefaultFlag (id),
    UNIQUE KEY (isDefault)
);

INSERT INTO PostalCodes (Zip, isDefault) VALUES ('123', 1   );
/* Only one row can be default */
INSERT INTO PostalCodes (Zip, isDefault) VALUES ('abc', 1   );
/* ERROR 1062 (23000): Duplicate entry '1' for key 'isDefault' */

/* MySQL allows multiple NULLs in unique indexes */
INSERT INTO PostalCodes (Zip, isDefault) VALUES ('456', NULL);
INSERT INTO PostalCodes (Zip, isDefault) VALUES ('789', NULL);

/* only NULL and 1 are admitted in isDefault thanks to foreign key */
INSERT INTO PostalCodes (Zip, isDefault) VALUES ('789', 2   );
/* ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`PostalCodes`, CONSTRAINT `PostalCodes_ibfk_1` FOREIGN KEY (`isDefault`) REFERENCES `DefaultFlag` (`id`))*/

আমার মনে হয়, ভুল হতে পারে এমন একমাত্র জিনিসটি যদি কেউ DefaultFlagটেবিলে একটি সারি যুক্ত করে । আমি মনে করি এটি কোনও বড় উদ্বেগ নয় এবং আপনি যদি নিরাপদে থাকতে চান তবে আপনি সর্বদা অনুমতি দিয়ে এটি ঠিক করতে পারেন।


0
SELECT ID, Zip FROM PostalCodes WHERE isDefault=True 

এটি মূলত আপনাকে সমস্যা দিচ্ছিল কারণ আপনি ক্ষেত্রটি ভুল জায়গায় রেখেছিলেন এবং এটিই।

এতে PostalCode, যে আইডি যা আপনি খুঁজছেন ঝুলিতে সঙ্গে একটি 'ডিফল্ট' টেবিল আছে। সাধারণভাবে, আপনার টেবিলে একটি রেকর্ড অনুসারে নামকরণ করাও ভাল, সুতরাং আপনার প্রাথমিক সারণির নামটি 'পোস্টকোড' বলে ভাল হবে। ডিফল্ট অবশ্যই একটি একক সারি টেবিল যতক্ষণ না আপনি (যেমন historics হিসাবে) কিছু অন্যান্য কনফিগারেশন বিরুদ্ধে আপনার ডিফল্ট বিশেষজ্ঞ প্রয়োজন হবে।

আপনি চান এমন চূড়ান্ত ক্যোয়ারীটি নিম্নরূপ:

select Zip from PostalCode p, Defaults d where p.ID=d.PostalCode;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.