এসকিউএল সার্ভার অপ্রত্যাশিত নির্বাচন ফলাফল (ডিবিএমএস ত্রুটি?)


37

নীচে সরল উদাহরণ দেওয়া হল, যা অদ্ভুত ফলাফলগুলি প্রত্যাশা করে এবং এটি আমাদের দলে ব্যাখ্যা করতে পারে না returns আমরা কি কিছু ভুল করছি বা এটি এসকিউএল সার্ভারের ত্রুটি?

কিছু তদন্তের পরে আমরা অনুসন্ধানের ক্ষেত্রটিকে ইউনিয়ন ধারাতে সাবকোয়ারিতে হ্রাস করেছি , যা "পুরুষ" সারণী থেকে একটি রেকর্ড নির্বাচন করে

এটি এসকিউএল সার্ভার 2000-তে প্রত্যাশার মতো কাজ করে (12 টি সারি দেয়) তবে ২০০৮ এবং ২০১২ সালে এটি কেবল একটি সারি দেয়।

create table dual (dummy int)

insert into dual values (0)

create table men (
man_id int,
wife_id int )

-- there are 12 men, 6 married 
insert into men values (1, 1)
insert into men values (2, 2)
insert into men values (3, null)
insert into men values (4, null)
insert into men values (5, null)
insert into men values (6, 3)
insert into men values (7, 5)
insert into men values (8, 7)
insert into men values (9, null)
insert into men values (10, null)
insert into men values (11, null)
insert into men values (12, 9)

এটি কেবল একটি সারিতে প্রত্যাবর্তন করে: 1 1 2

select 
man_id,
wife_id,
(select count( * ) from 
    (select dummy from dual
     union select men.wife_id  ) family_members
) as family_size
from men
--where wife_id = 2 -- uncomment me and try again

সর্বশেষ লাইনটি কমেন্ট করুন এবং এটি দেয়: 2 2 2

প্রচুর অদ্ভুত আচরণ রয়েছে:

  • "পুরুষ" টেবিলের উপর ধারাবাহিক ড্রপ, তৈরি, ছাঁটাই এবং সন্নিবেশ করার পরে এটি কখনও কখনও কাজ করে (12 সারি দেয়)
  • যখন আপনাকে "ইউনিয়ন নির্বাচন men.wife_id" "ইউনিয়ন সব নির্বাচন men.wife_id" বা "ইউনিয়ন নির্বাচন isnull (men.wife_id, নাল)" (!!!) এটি পরিবর্তন 12 ফেরৎ (প্রত্যাশিত হিসাবে) সারি।
  • আশ্চর্যজনক আচরণটি "স্ত্রী_আইডি" কলামের ডেটাটাইপের সাথে সম্পর্কিত নয় বলে মনে হচ্ছে। আমরা এটিকে ডেভলপমেন্ট সিস্টেমে অনেক বেশি ডেটা সেট সহ পর্যবেক্ষণ করেছি।
  • "যেখানে wife_id> 0" 6 টি সারি প্রদান করে
  • আমরা এই জাতীয় বিবৃতি দিয়ে অদ্ভুত আচরণের পর্যবেক্ষণ করি। নির্বাচন করুন * সারিগুলির উপসেট প্রদান করে, নির্বাচন করুন শীর্ষস্থানীয় 1000 প্রদান করে

উত্তর:


35

আমরা কি কিছু ভুল করছি বা এটি এসকিউএল সার্ভারের ত্রুটি?

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

বাগের জন্য তিনটি উপাদান প্রয়োজন:

  1. বাইরের রেফারেন্স সহ নেস্টেড লুপস (একটি প্রয়োগ)
  2. একটি অভ্যন্তরীণ দিকের অলস সূচক স্পুল যা বাইরের রেফারেন্সটি অনুসন্ধান করে
  3. একটি অভ্যন্তরীণ দিকের কনটেনটেশন অপারেটর

উদাহরণস্বরূপ, প্রশ্নের ক্যোয়ারী নিম্নলিখিতগুলির মতো একটি পরিকল্পনা তৈরি করে:

টিকাদিত পরিকল্পনা

এই উপাদানগুলির মধ্যে একটি অপসারণের অনেকগুলি উপায় রয়েছে, তাই বাগটি আর পুনরুত্পাদন করে না।

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

আরো বিস্তারিত

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

এই নির্দিষ্ট পরিকল্পনার আকারে সমস্যাটি দেখা দেয় যখন স্পুল পরীক্ষা করে দেখেছিল যে কোনও নতুন বহিরাগত রেফারেন্সটি আগে দেখা একইরকম কিনা। নেস্টেড লুপস যোগ দিন এর বাইরের রেফারেন্সগুলি সঠিকভাবে আপডেট করে এবং অপারেটরদের তাদের PrepRecomputeইন্টারফেস পদ্ধতির মাধ্যমে তার অভ্যন্তরীণ ইনপুটটিতে অবহিত করে। এই চেকের শুরুতে, অভ্যন্তরীণ পার্শ্ব অপারেটররা CParamBounds:FNeedToReloadশেষবারের চেয়ে বাইরের রেফারেন্সটি পরিবর্তিত হয়েছে কিনা তা দেখতে সম্পত্তিটি পড়েন । স্ট্যাকের একটি উদাহরণ নীচে দেখানো হয়েছে:

CParamBounds: FNeedToReload

CParamBounds:FNeedToReloadবাহ্যিক রেফারেন্স আসলেই বদলেছে কিনা তা নির্বিশেষে উপরের দেখানো সাবট্রিটি যখন উপস্থিত রয়েছে, বিশেষত যেখানে কনটেনটেশন ব্যবহার করা হয়, তখন কিছু ভুল হয় (সম্ভবত একটি বাইওয়াল / বাইআরফ / কপি সমস্যা) যা সর্বদা মিথ্যা দেয়।

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

নেট ফলাফলটি হ'ল সমস্যাযুক্ত পরিকল্পনার আকারের অলস সূচক স্পুল সর্বদা মনে করে যে এটি ইতিমধ্যে বর্তমান বাহ্যিক রেফারেন্সটি দেখে ফেলেছে, তার কাজের সারণীতে সন্ধান করে পুনরায় রাইন্ড করে, সাধারণত কিছুই খুঁজে পায় না, সুতরাং বাহ্যিক রেফারেন্সের জন্য কোনও সারি ফিরে আসে না। একটি ডিবাগারে মৃত্যুদন্ড কার্যকর করার সময়, স্পুল কেবল কখনও তার RewindHelperপদ্ধতিটি চালায় এবং কখনও তার ReloadHelperপদ্ধতি (পুনরায় লোড = এই প্রসঙ্গে পুনরায় প্রত্যাবর্তন) করে না। এটি কার্যকর করার পরিকল্পনায় স্পষ্ট হয় কারণ স্পুলের অধীনে অপারেটরদের সকলের 'মৃত্যুর সংখ্যা = 1' থাকে।

RewindHelper

ব্যতিক্রম অবশ্যই, অলস সূচক স্পুল প্রদত্ত প্রথম বাহ্যিক রেফারেন্সের জন্য। এটি সর্বদা সাবট্রি চালায় এবং কাজের সারণীতে ফলাফলের সারিটি ক্যাশে করে। পরবর্তী সমস্ত পুনরাবৃত্তির ফলস্বরূপ একটি রিওয়াইন্ড তৈরি হয়, যা কেবলমাত্র একটি সারি (একক ক্যাশেড সারি) উত্পাদন করে যখন বর্তমান পুনরাবৃত্তিটি প্রথমবারের মতো বাইরের রেফারেন্সের জন্য একই মান রাখে।

সুতরাং, নেস্টেড লুপস জোনের বাইরের দিকে যে কোনও প্রদত্ত ইনপুট সেট করার জন্য, প্রথম সারির ডুপ্লিকেটগুলি প্রক্রিয়াযুক্ত হওয়াতে কোয়েরিটি যতগুলি সারি ফিরে আসবে (অবশ্যই প্রথম সারির জন্য প্লাস ওয়ান)।

ডেমো

সারণী এবং নমুনা ডেটা:

CREATE TABLE #T1 
(
    pk integer IDENTITY NOT NULL,
    c1 integer NOT NULL,

    CONSTRAINT PK_T1
    PRIMARY KEY CLUSTERED (pk)
);
GO
INSERT #T1 (c1)
VALUES
    (1), (2), (3), (4), (5), (6),
    (1), (2), (3), (4), (5), (6),
    (1), (2), (3), (4), (5), (6);

নিম্নলিখিত (তুচ্ছ) কোয়েরিটি মার্জ ইউনিয়ন ব্যবহার করে প্রতিটি সারির (মোট 18 টি) জন্য সঠিক দুটি গণনা তৈরি করে:

SELECT T1.c1, C.c1
FROM #T1 AS T1
CROSS APPLY 
(
    SELECT COUNT_BIG(*) AS c1
    FROM
    (
        SELECT T1.c1
        UNION
        SELECT NULL
    ) AS U
) AS C;

ইউনিয়ন পরিকল্পনা মার্জ করুন

যদি আমরা এখন একটি জোটকে জোর করার জন্য একটি কোয়েরি ইঙ্গিতটি যুক্ত করি:

SELECT T1.c1, C.c1
FROM #T1 AS T1
CROSS APPLY 
(
    SELECT COUNT_BIG(*) AS c1
    FROM
    (
        SELECT T1.c1
        UNION
        SELECT NULL
    ) AS U
) AS C
OPTION (CONCAT UNION);

সম্পাদন পরিকল্পনার সমস্যাযুক্ত আকার রয়েছে:

সংঘটন পরিকল্পনা

এবং ফলাফলটি এখন ভুল, মাত্র তিনটি সারি:

তিনটি সারির ফলাফল

যদিও এই আচরণের গ্যারান্টি নেই তবে ক্লাস্টারড ইনডেক্স স্ক্যানের প্রথম সারির c1মান 1 রয়েছে। এই মান সহ আরও দুটি সারি রয়েছে, সুতরাং মোট তিনটি সারি উত্পাদিত হয়।

এখন ডেটা টেবিলটি কেটে নিন এবং এটি 'প্রথম' সারির আরও নকল সহ লোড করুন:

TRUNCATE TABLE #T1;

INSERT #T1 (c1)
VALUES
    (1), (2), (3), (4), (5), (6),
    (1), (2), (3), (4), (5), (6),
    (1), (1), (1), (1), (1), (1);

এখন কনেকেটেনশন পরিকল্পনাটি হ'ল:

8 সারি সংমিশ্রণ পরিকল্পনা

এবং যেমন ইঙ্গিত করা হয়েছে, 8 টি সারি c1 = 1অবশ্যই প্রস্তুত করা হয়:

8 সারির ফলাফল

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


এই ভুল-ফলাফল বাগটি কোনও পর্যায়ে স্থির হয়েছিল। এটি আর 2012 থেকে এসকিউএল সার্ভারের কোনও সংস্করণে আমার জন্য পুনরুত্পাদন করে না। এটি এসকিউএল সার্ভার 2008 আর 2 এসপি 3-জিডিআর বিল্ড 10.50.6560.0 (এক্স 64) এ রিপ্রো করে।


-3

আপনি বিবৃতিটি না ছাড়াই কেন সাবকিউরি ব্যবহার করেন? আমি মনে করি যে এটি 2005 এবং 2008 এর সার্ভারগুলিতে পার্থক্যের কারণ হতে পারে। সম্ভবত আপনি সুস্পষ্ট যোগদানের সাথে যেতে পারেন?

select 
m1.man_id,
m1.wife_id,
(select count( * ) from 
    (select dummy from dual
     union
     select m2.wife_id
     from men m2
     where m2.man_id = m1.man_id) family_members
) as family_size
from men m1

3
হ্যাঁ, এটি কাজ করে তবে আমার সংস্করণটিও কাজ করা উচিত। বিমূর্ত উদাহরণের উপরে আমাদের উত্পাদন ক্যোয়ারির অনেক সরলীকৃত সংস্করণ, যা অনেক বেশি অর্থবোধ করে।
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.