مثالهای عملی از مقدماتی تا حرفهای
مثال 1: تبدیل عناصر به چند ردیف
سه عنصر item به سه ردیف رابطهای تبدیل میشوند.
DECLARE @x xml=N'<items><item id="1"/><item id="2"/><item id="3"/></items>';
SELECT N.I.value('(@id)[1]','int') AS ItemID
FROM @x.nodes('/items/item') AS N(I);
| خروجی نمونه | تفسیر نتیجه |
|---|
| سه ردیف: 1، 2، 3 | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: Alias N(I) الزامی است و I Context هر item را برای value() فراهم میکند.
مثال 2: خردکردن ستون XML جدول
خطوط هر سفارش همراه کلید رابطهای والد استخراج میشوند.
DECLARE @Orders table(OrderID int, Doc xml);
INSERT INTO @Orders VALUES(10,N'<o><line sku="A"/><line sku="B"/></o>');
SELECT O.OrderID, X.L.value('(@sku)[1]','nvarchar(20)') AS SKU
FROM @Orders AS O
CROSS APPLY O.Doc.nodes('/o/line') AS X(L);
| خروجی نمونه | تفسیر نتیجه |
|---|
| OrderID=10 با SKUهای A و B | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: نگهداشتن OrderID اتصال ردیف خردشده به موجودیت والد را تضمین میکند.
مثال 3: استخراج Element و Attribute
از هر محصول هم شناسه Attribute و هم نام Element خوانده میشود.
DECLARE @x xml=N'<products><p id="7"><name>Mouse</name></p></products>';
SELECT P.N.value('(@id)[1]','int') AS ID,
P.N.value('(name/text())[1]','nvarchar(100)') AS Name
FROM @x.nodes('/products/p') AS P(N);
| خروجی نمونه | تفسیر نتیجه |
|---|
| ID=7، Name=Mouse | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: مسیرهای داخل value() نسبت به Context جاری هر p نوشته میشوند.
مثال 4: حفظ والد بدون فرزند با OUTER APPLY
هر دو سفارش باید گزارش شوند، حتی اگر line نداشته باشند.
DECLARE @Orders table(ID int, Doc xml);
INSERT INTO @Orders VALUES(1,N'<o><line sku="A"/></o>'),(2,N'<o/>');
SELECT O.ID, X.L.value('(@sku)[1]','nvarchar(10)') AS SKU
FROM @Orders AS O
OUTER APPLY O.Doc.nodes('/o/line') AS X(L);
| خروجی نمونه | تفسیر نتیجه |
|---|
| ID=1 با A و ID=2 با NULL | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: اگر CROSS APPLY استفاده میشد، سفارش دوم از نتیجه حذف میشد.
مثال 5: دو سطح CROSS APPLY
گروهها و آیتمهای داخل هر گروه در سطح جداگانه خرد میشوند.
DECLARE @x xml=N'<catalog><g name="G1"><i>A</i><i>B</i></g></catalog>';
SELECT G.N.value('(@name)[1]','nvarchar(20)') AS GroupName,
I.N.value('(text())[1]','nvarchar(20)') AS ItemName
FROM @x.nodes('/catalog/g') AS G(N)
CROSS APPLY G.N.nodes('i') AS I(N);
| خروجی نمونه | تفسیر نتیجه |
|---|
| G1-A و G1-B | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: Apply دوم نسبت به Context گروه اجرا میشود و رابطه سلسلهمراتبی را حفظ میکند.
مثال 6: رفتار با XML تهی
یک ردیف والد XML تهی دارد و اثر CROSS APPLY مشاهده میشود.
DECLARE @T table(ID int, Doc xml);
INSERT INTO @T VALUES(1,NULL),(2,N'<r><n>5</n></r>');
SELECT T.ID, X.N.value('(text())[1]','int') AS V
FROM @T AS T
CROSS APPLY T.Doc.nodes('/r/n') AS X(N);
| خروجی نمونه | تفسیر نتیجه |
|---|
| فقط ID=2 و V=5 | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: برای حفظ ID=1 باید OUTER APPLY انتخاب شود و منطق NULL جداگانه تعریف شود.
مثال 7: اعمال Predicate داخل nodes()
فقط آیتمهایی که مبلغشان بیش از صد است به ردیف تبدیل میشوند.
DECLARE @x xml=N'<items><i amount="60"/><i amount="140"/></items>';
SELECT X.N.value('(@amount)[1]','int') AS Amount
FROM @x.nodes('/items/i[@amount > 100]') AS X(N);
| خروجی نمونه | تفسیر نتیجه |
|---|
| Amount = 140 | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: فیلتر زودهنگام تعداد Contextها و هزینه پردازش مراحل بعد را کاهش میدهد.
مثال 8: سناریوی ETL سفارش
داده XML به شکل مناسب برای درج در جدول Stage استخراج میشود.
DECLARE @x xml=N'<orders><order id="1"><total>50</total></order><order id="2"><total>80</total></order></orders>';
DECLARE @Stage table(OrderID int, Total decimal(10,2));
INSERT INTO @Stage
SELECT X.N.value('(@id)[1]','int'), X.N.value('(total/text())[1]','decimal(10,2)')
FROM @x.nodes('/orders/order') AS X(N);
SELECT * FROM @Stage;
| خروجی نمونه | تفسیر نتیجه |
|---|
| دو ردیف 1-50 و 2-80 | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: اعتبارسنجی و Transaction در ETL واقعی باید پیش از انتقال Stage به جدول مقصد اضافه شود.
مثال 9: اصلاح Alias فراموششده
نحو صحیح با نام مستعار جدول و ستون نشان داده میشود.
DECLARE @x xml=N'<r><v>1</v></r>';
-- nodes() یک Rowset بدون نام میدهد؛ Alias الزامی است
SELECT X.Node.value('(text())[1]','int') AS V
FROM @x.nodes('/r/v') AS X(Node);
| خروجی نمونه | تفسیر نتیجه |
|---|
| V = 1 | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: نامگذاری واضح X(Node) خطا را رفع و خوانایی Queryهای چند APPLY را بهتر میکند.
مثال 10: کنترل افزایش ردیف برای کارایی
فقط والدهای فعال و سپس فقط گرههای مهم خرد میشوند.
DECLARE @T table(ID int, Active bit, Doc xml);
INSERT INTO @T VALUES(1,1,N'<r><n important="1">A</n><n>B</n></r>'),(2,0,N'<r><n important="1">C</n></r>');
SELECT T.ID, X.N.value('(text())[1]','nvarchar(20)') AS V
FROM @T AS T
CROSS APPLY T.Doc.nodes('/r/n[@important="1"]') AS X(N)
WHERE T.Active=1;
| خروجی نمونه | تفسیر نتیجه |
|---|
| ID=1، V=A | نتیجه مورد انتظار پس از اجرای Query در SQL Server |
نکته کاربردی: ترکیب فیلتر رابطهای و Predicate XQuery از تولید ردیفهای بیمصرف جلوگیری میکند.
سؤالات متداول
۱. nodes() در SQL Server دقیقاً چه کاری انجام میدهد؟
متد nodes() یک Rowset از کپیهای منطقی گرههای انتخابشده میسازد و پل اصلی میان ساختار سلسلهمراتبی XML و پردازش رابطهای SQL Server است. انتخاب این متد باید بر اساس نوع خروجی موردنیاز باشد، نه صرفاً کوتاهتر بودن Query.
۲. مهمترین نکته آموزشی هنگام نوشتن nodes() چیست؟
nodes() معمولاً با CROSS APPLY یا OUTER APPLY روی ستون XML جدول استفاده میشود. بهتر است این قاعده با تستهای کوچک روی NULL، گره غایب و چند گره تثبیت شود.
۳. آیا آموزش سازمانی nodes() برای تیمهای داده ارزش تجاری دارد؟
بله؛ خطا در استفاده از nodes() میتواند هزینه پردازش، خطای گزارش و زمان پشتیبانی را بالا ببرد. یک کارگاه مبتنی بر Queryهای واقعی سازمان معمولاً سریعتر از آموزش صرفاً نظری به نتیجه میرسد.
۴. چه زمانی بازبینی حرفهای Queryهای nodes() مقرونبهصرفه است؟
وقتی جدول بزرگ، XML Index، گزارش حساس یا SLA جدی دارید، بازبینی Execution Plan و اندازهگیری CPU و Logical Reads میتواند هزینه توسعه و زیرساخت را کاهش دهد.
۵. nodes() با متدهای دیگر XML چه تفاوتی دارد؟
nodes() برای «تبدیل گرههای XML به ردیفهای رابطهای» طراحی شده است؛ value() اسکالر میدهد، query() قطعه XML میسازد، exist() وجود را میآزماید، nodes() ردیف تولید میکند و modify() داده را تغییر میدهد.
۶. برای پیادهسازی nodes() در یک پروژه واقعی از کجا شروع کنیم؟
ابتدا قرارداد XML، Namespace، حجم اسناد و Queryهای پرتکرار را ثبت کنید؛ سپس نمونه کوچک، تست صحت، Baseline کارایی و برنامه استقرار مرحلهای بسازید. در پروژه حساس، مشاوره SQL Server باید مبتنی بر همین شواهد باشد.
۷. خطای رایج nodes() چیست؟
فراموش کردن Alias جدول و ستون خطای نحوی ایجاد میکند. پیام خطا، XQuery دقیق و نمونه XML مسئلهدار را کنار هم نگه دارید تا علت بهجای حدسزدن قابل بازتولید باشد.
۸. چگونه Performance متد nodes() را بررسی کنیم؟
تا حد ممکن Predicate را داخل XQuery قرار دهید تا گرههای کمتری Shred شوند. از SET STATISTICS IO,TIME ON، Actual Execution Plan و Query Store برای مقایسه قبل و بعد استفاده کنید.
۹. بهترین روش استفاده از nodes() چیست؟
Aliasهای معنادار مانند ItemNode(ItemXml) انتخاب کنید. علاوه بر آن، تست واحد دادههای مرزی و مستندسازی Namespace مانع بازگشت خطا در نسخههای بعد میشود.
۱۰. nodes() با کدام نسخههای SQL Server سازگار است؟
متدهای نوع داده xml از SQL Server 2005 در دسترساند، اما رفتار دقیق Optimizer و مزیت ایندکسها را باید روی نسخه و Compatibility Level محیط مقصد آزمایش کرد. پیش از مهاجرت نیز Regression Test اجرا کنید.