פניה משרת 2005 לגרסה מתקדמת יותר דרך Linked Server: בעיית An invalid tabular data stream (TDS) collation was encountered

03/07/2014

אין תגובות

2005? תזכירו לי מתי זה היה?..
יש עדיין התקנות של 2005 בעולם וגם של גרסאות מוקדמות יותר, צריך לתמוך בהן, וכל זה בסביבה בה יש גם גרסאות מתקדמות יותר; וכתוצאה מכך העניינים קצת חורקים.
הדוגמאות שלהלן הורצו על שרת 2005, המחובר דרך Linked Server לשרת 2012; ואני מציג כאן תהליך מתגלגל של פתרון מאולתר של בעיות שמסתיים בהצלחה. יתכן ויש פתרונות מסודרים יותר, אך לא הצלחתי למצוא כאלה בעצמי או ברשת, ולמי שיהיו הצעות משופרות – אשמח לשמוע.

נתחיל בשליפה פשוטה:

Select    database_id

From    [Server2012].master.sys.databases;

image

זה כן יעבוד בעזרת OpenQuery (וכפי שנראה בהמשך- גם בעזרת Execute .. As):

Select    *

From    OpenQuery([ISR-SR-PUDEL-1\ISR_SQL2012],

                        'Select database_id

                        From    master.sys.databases;') T;

image

תתעורר בעייה עם עמודות טקסטואליות בשל אי תאימות של Collation:

Select    *

From    OpenQuery([Server2012],

                'Select database_id,

                        name

                From    master.sys.databases;') T;

image

נצטרך לטפל בבעיית ה-Collation:

Select    *

From    OpenQuery([Server2012],

                'Select database_id,

                        name Collate Database_Default

                From    master.sys.databases;') T;

image

מה יקרה אם נרצה להוסיף לכך פרמטר? אפשר למשל כך:

Declare    @DB Int;

Set        @DB=2;

Select    *

From    OpenQuery([Server2012],

                'Select database_id,

                        name Collate Database_Default

                From    master.sys.databases;') T

Where    database_id<=@DB;

image

הבעייה בפתרון כזה היא שכשמדובר בטבלה בת חצי מיליארד שורות שצריך לשלוף ממנה שתיים – כל החצי מיליארד נשלפות ועוברות ברשת, ורק בשרת המקומי מתבצע הפילטור.
להכניס את הפרמטר לתוך מחרוזת ה-SQL (כדי שהשליפה בשרת המקור כבר תפולטר) אי אפשר, אז ננסה לשרשר את הכל לתוך משתנה ובו להשתמש ב-OpenQuery:

Declare    @DB Int;

Set        @DB=2;

Declare    @SQL Varchar(Max);

Set        @SQL=

'Select database_id,

        name Collate Database_Default

From    master.sys.databases

Where    database_id<='+Cast(@DB As Varchar(Max))+';';

Select    *

From    OpenQuery([Server2012],

                        @SQL) T;

image

לא ניתן להכניס לתוך OpenQuery משתנה, אלא רק מחרוזת מפורשת.
הפתרון הוא להשתמש ב-Execute .. As:

Declare    @DB Int;

Set        @DB=2;

Declare    @SQL Varchar(Max);

Set        @SQL=

'Select database_id,

        name Collate Database_Default

From    master.sys.databases

Where    database_id<='+Cast(@DB As Varchar(Max))+';';

Exec(@SQL) At [Server2012];

image

בעייה זו נפתרה אם כך.
כעת אנחנו רוצים לבצע Join בין השליפה הזו וטבלה מקומית. כפי שזה כתוב כעת זה לא יכול להופיע בתוך Select, ולכן הפתרון הוא להפנות את הפלט לטבלה זמנית, ולבצע Join איתה:

Declare    @DB Int;

Set        @DB=2;

Declare    @SQL Varchar(Max);

Set        @SQL=

'Select database_id,

        name Collate Database_Default

From    master.sys.databases

Where    database_id<='+Cast(@DB As Varchar(Max))+';';

If Object_ID('tempdb..#DB','U') Is Not Null Drop Table #DB;

Create    Table #DB(database_id Int Primary Key,

                name Sysname);

Insert

Into    #DB

Exec(@SQL) At [Server2012];

image

בשל בעיית סנכרון טרנזקציות בין השרתים – אנחנו מקבלים הודעת שגיאה.
לא מצאתי לזה פתרון באחד ה-Properties של ה-Linked Server (שהוגדר כאמור בשרת 2005):

image

אין ברירה אלא לאלתר פתרון בעזרת ה-OpenQuery: “נדחוף” את כולו (לא רק את ה-SQL!) לתוך משתנה, ואת זה נריץ בעזרת ה-Execute:

Declare    @DB Int;

Set        @DB=2;

Declare    @SQL Varchar(Max);

Set        @SQL=

'Select    *

From    OpenQuery([Server2012],

                        ''Select database_id,

                                name Collate Database_Default

                        From    master.sys.databases

                        Where    database_id<='+Cast(@DB As Varchar(Max))+';'') T;';

If Object_ID('tempdb..#DB','U') Is Not Null Drop Table #DB;

Create    Table #DB(database_id Int Primary Key,

                name Sysname);

Insert

Into    #DB

Exec(@SQL);

Select    *

From    sys.databases T1

Inner Join #DB T2

        On T1.name=T2.name;

image

הפעם זה עבד והבעייה נפתרה.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *