سؤال

واجهت شيئًا غريبًا بعض الشيء هذا الصباح واعتقدت أنني سأقدمه للتعليق.

هل يمكن لأي شخص أن يشرح سبب قيام استعلام SQL التالي بطباعة "متساوية" عند التشغيل مقابل SQL 2008. يتم تعيين مستوى توافق DB على 100.

if '' = ' '
    print 'equal'
else
    print 'not equal'

وهذا يعود 0:

select (LEN(' '))

يبدو أنه يتم تقليم المساحة التلقائية. ليس لدي أي فكرة عما إذا كان هذا هو الحال في الإصدارات السابقة من SQL Server ، ولم يعد لدي أي شيء حتى لاختباره.

واجهت هذا لأن استعلام الإنتاج كان يعيد نتائج غير صحيحة. لا يمكنني العثور على هذا السلوك موثق في أي مكان.

هل لدى أي شخص أي معلومات عن هذا؟

هل كانت مفيدة؟

المحلول

varcharS والمساواة شائكة في TSQL. ال LEN الوظيفة تقول:

إرجاع عدد الأحرف ، بدلاً من عدد البايتات ، من تعبير السلسلة المعطى ، باستثناء الفراغات الزائدة.

تحتاج إلى استخدام DATALENGTH للحصول على صحيح byte عدد البيانات المعنية. إذا كان لديك بيانات Unicode ، لاحظ أن القيمة التي تحصل عليها في هذا الموقف لن تكون هي نفسها طول النص.

print(DATALENGTH(' ')) --1
print(LEN(' '))        --0

عندما يتعلق الأمر بمساواة التعبيرات ، تتم مقارنة السلاسل بالمساواة مثل هذا:

  • احصل على سلسلة أقصر
  • وسادة مع الفراغات حتى الطول يساوي هذا السلسلة الطويلة
  • قارن الاثنين

إنها الخطوة المتوسطة التي تسبب نتائج غير متوقعة - بعد تلك الخطوة ، تقارن بفعالية المسافة البيضاء ضد المسافة البيضاء - وبالتالي يُنظر إليها على أنها متساوية.

LIKE يتصرف بشكل أفضل من = في موقف "الفراغات" لأنه لا يؤدي إلى وضع فارغ على النمط الذي كنت تحاول مطابقته:

if '' = ' '
print 'eq'
else
print 'ne'

سنعطي eq في حين:

if '' LIKE ' '
print 'eq'
else
print 'ne'

سنعطي ne

حذرا مع LIKE على الرغم من ذلك: إنه ليس متماثلًا: فهو يعامل المسافة البيضاء المتخلف على أنه مهم في النمط (RHS) ولكن ليس التعبير المطابق (LHS). ما يلي مأخوذ من هنا:

declare @Space nvarchar(10)
declare @Space2 nvarchar(10)

set @Space = ''
set @Space2 = ' '

if @Space like @Space2
print '@Space Like @Space2'
else
print '@Space Not Like @Space2'

if @Space2 like @Space
print '@Space2 Like @Space'
else
print '@Space2 Not Like @Space'

@Space Not Like @Space2
@Space2 Like @Space

نصائح أخرى

= المشغل هو T-SQL ليس "متساويًا" بقدر ما هو "نفس الكلمة/العبارة ، وفقًا لتجميع سياق التعبير" ، ولين هو "عدد الأحرف في الكلمة/العبارة". لا توجد مجموعات تعامل مع الفراغات المتأخرة كجزء من الكلمة/العبارة التي تسبقها (على الرغم من أنها تعامل الفراغات الرائدة كجزء من السلسلة التي تسبقها).

إذا كنت بحاجة إلى تمييز "هذا" عن "هذا" ، فيجب ألا تستخدم "نفس الكلمة أو العبارة" لأن "هذا" و "هذا" هما نفس الكلمة.

المساهمة في الطريقة = الأعمال هي فكرة أن مشغل المساواة في السلسلة يجب أن يعتمد على محتويات وسيطه وعلى سياق ترتيب التعبير ، ولكن لا ينبغي أن يعتمد على أنواع الوسائط ، إذا كانت كلاهما نوعين من السلسلة .

إن مفهوم اللغة الطبيعية لـ "هذه هي نفس الكلمة" ليس دقيقًا بما يكفي ليكون قادرًا على التقاطه بواسطة مشغل رياضي مثل = ، وليس هناك مفهوم لنوع السلسلة في اللغة الطبيعية. السياق (أي ، الترتيب) مسائل (ووجهة في اللغة الطبيعية) وهي جزء من القصة ، والخصائص الإضافية (بعضها يبدو غريبًا) جزء من تعريف = من أجل جعلها محددة جيدًا في العالم غير الطبيعي البيانات.

في مشكلة النوع ، لا تريد أن تتغير الكلمات عندما يتم تخزينها في أنواع سلسلة مختلفة. على سبيل المثال ، يمكن أن يحمل كل من الأنواع Varchar (10) و Char (10) و Char (3) تمثيلات كلمة "Cat" ، و؟ = يجب أن ندعنا نقرر ما إذا كانت قيمة أي من هذه الأنواع تحمل كلمة "القط" (مع قضايا الحالة واللكنة التي تحددها الترتيب).

الرد على تعليق Johnfx:

يرى باستخدام بيانات char و varchar في الكتب على الإنترنت. نقلاً عن تلك الصفحة ، التأكيد لي:

كل قيمة بيانات char و varchar لها ترتيب. تحدد المجموعات سمات مثل أنماط البتات المستخدمة لتمثيل كل حرف ، قواعد المقارنة, والحساسية للحالة أو الالتزام.

أوافق على أنه قد يكون من الأسهل العثور عليه ، ولكن تم توثيقه.

تجدر الإشارة ، أيضًا ، أن دلالات SQL ، حيث = تتعلق بالبيانات في العالم الحقيقي وسياق المقارنة (على عكس شيء عن البتات المخزنة على الكمبيوتر) كان جزءًا من SQL لفترة طويلة. فرضية RDBMSS و SQL هي التمثيل المؤمن للبيانات الواقعية ، ومن ثم دعمها للترجمات قبل سنوات عديدة قبل أن يدخل الأفكار المماثلة (مثل CultureInfo) عالم اللغات الشبيهة بالجول. كانت فرضية تلك اللغات (على الأقل حتى وقت قريب جدًا) تحل المشكلات في الهندسة ، وليس إدارة بيانات الأعمال. (في الآونة الأخيرة ، فإن استخدام لغات مماثلة في التطبيقات غير الهندسية مثل البحث هو بعض الغزو ، لكن Java و C#وما زالت تكافح مع جذورها غير التجارية.)

في رأيي ، ليس من العدل انتقاد SQL لكونه مختلفًا عن "معظم لغات البرمجة". تم تصميم SQL لدعم إطار لنمذجة بيانات الأعمال يختلف تمامًا عن الهندسة ، وبالتالي فإن اللغة مختلفة (والأفضل لهدفها).

Heck ، عندما تم تحديد SQL لأول مرة ، لم يكن لدى بعض اللغات أي نوع سلسلة مدمجة. وفي بعض اللغات ، لا يزال المشغل المتساوي بين السلاسل لا يقارن بيانات الأحرف على الإطلاق ، ولكنه يقارن المراجع! لن يفاجئني إذا كانت الفكرة التي تعتمد على الثقافة في عقد أو عامين آخر.

لقد وجدت هذا مقالة مدونة الذي يصف السلوك ويشرح السبب.

يتطلب معيار SQL أن مقارنات السلسلة ، بفعالية ، قم بتوصيل السلسلة الأقصر مع أحرف الفضاء.يؤدي هذا إلى النتيجة المدهشة التي تفيد بأن n '' = n '' (السلسلة الفارغة تساوي سلسلة من أحرف الفضاء واحدة أو أكثر) وبشكل أعم أي سلسلة تساوي سلسلة أخرى إذا كانت تختلف فقط عن طريق المساحات المتأخرة. يمكن أن تكون هذه مشكلة في بعض السياقات.

مزيد من المعلومات المتاحة أيضا في MSKB316626

كان هناك سؤال مماثل منذ فترة حيث نظرت إلى مشكلة مماثلة هنا

بدلاً من Len ('') ، استخدم Datalength ('') - الذي يمنحك القيمة الصحيحة.

كانت الحلول لاستخدام جملة مثل كما هو موضح في إجابتي هناك ، و/أو تضمين شرط الثاني في جملة WHERN للتحقق من Datalength أيضًا.

اقرأ هذا السؤال والروابط هناك.

لمقارنة قيمة بمساحة حرفية ، يمكنك أيضًا استخدام هذه التقنية كبديل لبيان ما شابه:

IF ASCII('') = 32 PRINT 'equal' ELSE PRINT 'not equal'

في بعض الأحيان يتعين على المرء أن يتعامل مع المساحات في البيانات ، مع أو بدون أي شخصيات أخرى ، على الرغم من أن فكرة استخدام NULL أفضل - ولكن ليس دائمًا صالحًا للاستخدام. واجهت الموقف الموصوف وحلته بهذه الطريقة:

... حيث ('>' + space + '<') <> ('>' + @ @Space2 + '<')

بالطبع لن تفعل ذلك كمية كبيرة من البيانات ولكنها تعمل بسرعة وسهولة لبعض الخطوط ...

هربرت

كيفية تسجيل سجلات متميزة على Select with Fields char/varchar على SQL Server: مثال:

declare @mayvar as varchar(10)

set @mayvar = 'data '

select mykey, myfield from mytable where myfield = @mayvar

مُتوقع

Mykey (int) | MyField (varchar10)

1 | 'البيانات '

تم الحصول عليها

Mykey | مجالي

1 | 'البيانات' 2 | 'البيانات '

حتى لو كتبتselect mykey, myfield from mytable where myfield = 'data' (بدون فارغ نهائي) أحصل على نفس النتائج.

كيف حللت؟ في هذا الوضع:

select mykey, myfield
from mytable
where myfield = @mayvar 
and DATALENGTH(isnull(myfield,'')) = DATALENGTH(@mayvar)

وإذا كان هناك فهرس على MyField ، فسيتم استخدامه في كل حالة.

آمل أن يكون مفيدا.

هناك طريقة أخرى هي إعادتها إلى حالة أن المساحة لها قيمة. على سبيل المثال: استبدل المساحة بحرف معروف مثل _

if REPLACE('hello',' ','_') = REPLACE('hello ',' ','_')
    print 'equal'
else
    print 'not equal'

عائدات: لا تساوي

ليست مثالية ، وربما بطيئة ، ولكنها طريقة سريعة أخرى إلى الأمام عند الحاجة بسرعة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top