‫۱۰ سال و ۸ ماه قبل، شنبه ۱۹ بهمن ۱۳۹۲، ساعت ۱۸:۳۳
در نسخه 2012 جهت سهولت در مهاجرت پایگاه داده‌های Access به SQL Server از توابع CHOOSE و IIF حمایت شده.
 
منتها تابع IIF چندان انعطاف پذیر نیست. مثلا اگر بخواهید به ازای چند حالت مشخص از داده‌های یک فیلد یک مقدار را برگردانید مجبورید چند تابع IIF تودرتو بنویسید. تودرتو بودن این تابع هم به 10 سطح محدود میشه.
اما CASE قابلیت‌ها و انعطاف پذیری بیشتری داره.
سوال میشه گاها کدام یک از این دو Performance یا کارایی بهتر دارد، در جواب میشه گفت هر دو برابر اند. در واقع IIF هنگام اجرا تبدیل به فرم CASE خواهد شد.
فرض کنید یک نظر سنجی تلوزیونی تنظیم کردیم که مردم از طریق پیامک نظر خودشان را به ما اعلام میکنند. شش گزینه هم داریم. برای انتخاب هر گزینه کافیست از اعداد 1 تا 6 استفاده کنیم. حال هنگام نمایش می‌خواهیم به جای اعداد مقدار متناظر ظاهر شود:

Use Tempdb
Go

CREATE TABLE [Sample] (value int);
INSERT INTO [Sample] VALUES (1),(2),(3),(4),(5),(6);
Go

--simple CASE Expression
SELECT value,
       CASE Value 
        WHEN 1 THEN 'Very Bad'
        WHEN 2 THEN 'Bad'
        WHEN 3 THEN 'Not Bad'
        WHEN 4 THEN 'Good'
        WHEN 5 THEN 'Very Good'
        WHEN 6 THEN 'Excellent'
       ELSE NULL
END AS [Result]
FROM [Sample];

--CHOOSE Scalar Function
SELECT value,
       CHOOSE(value,'Very Bad','Bad','Not Bad','Good','Very Good','Excellent')
FROM [Sample];


--nested IIF Scalr Function
SELECT value, 
IIF(value = 1, 'Very Bad',
  IIF(value = 2, 'Bad',
    IIF(value = 3, 'Not Bad',
      IIF(value = 4, 'Good',
        IIF(value = 5, 'Very Good', 'Excellent'
           )
         )
       )
     )
   )
FROM [Sample];
‫۱۰ سال و ۸ ماه قبل، جمعه ۱۸ بهمن ۱۳۹۲، ساعت ۱۸:۴۵
سلام اسحق،
در مثالی که من تهیه کردم، میانگین داده‌های مربوط به 3 سطر قبل محاسبه شده، بدون لحاظ مقدار جاری. اما در مساله مربوط به آن سایت میانگین داده‌های مرتبط به 19 سطر قبل و سطر جاری محاسبه شده.
و همچنین در بخش Specification مربوط به آن تابع میانگین، در مثال سایت از PARTITION استفاده شده آن هم به این خاطر که داده‌های جدول به گروه‌های مختلفی بر اساس مقادیر ستون StockId تقسیم شده است. و میخواسته میانگین مرتبط به هر StockId بطور مجزا محاسبه بشه. در واقع نتیجه را به تعداد StorckId‌ها بخش بندی کرده.

نام مستعاری که به جدول Quotes داده شده، غیر ضروری بوده، چرا که تنها یک جدول بیشتر در Query مشارکت نداشته و نیازی به ذکر نام جدول یا نام مستعار جدول نیست.
همچنین برای شفافیت بیشتر و ابهام زدایی، بهتر است قسمت Rows تابع تجمعی را کامل و صریح بنویسیم به این صورت:
SELECT StockId, QuoteId, QuoteDay, QuoteClose,
       AVG (QuoteClose) OVER (PARTITION BY StockId 
                              ORDER BY QuoteId 
                               ROWS BETWEEN 19 PRECEDING AND CURRENT ROW) AS MA20
FROM dbo.Quotes AS T0;
‫۱۱ سال و ۲ ماه قبل، یکشنبه ۲۷ مرداد ۱۳۹۲، ساعت ۰۶:۵۱
بله همینطوره.
سایت ویکی توضیحات خوبی راجب معایب و مزایای استفاده از surrogate key داده.
یه مرور بسیار جزئی که به معایب و مزایا داشتم متوجه شدم که در پست قبلیم از معایبش به normalization و از مزایاش به performance اش اشاره ای داشتم.

‫۱۱ سال و ۲ ماه قبل، یکشنبه ۲۷ مرداد ۱۳۹۲، ساعت ۰۳:۰۲
این امکان هم وجود داره که یک ستون دیگه به جدول اضافه کنید و آن را به عنوان PK در نظر بگیرید. اما باید به این نکته بسیار مهم نیز توجه داشته باشید که نمیشه آن دو ستون (کددانشجو و ترم) را همینطور به حال خود رها کرد. با این فرض که با اضافه شدن این ستون دیگه هیچ دو سطر تکراری به خاطر uniqueness بودن PK نخواهیم داشت.
شما لازمه که یک قید منحصربفرد تکریبی در کنار PK برای آن دو ستون در جدول ایجاد کنید. تا به ازای یک ترم معین و یک دانشجو معین تنها یک معدل ثبت بشه.
پس با لحاظ توضیحات فوق جدول به این شکل در می‌آید:
create table Avgs
(
   identifier int not null identity(1,1) primary key,
   student_id varchar(10) not null
        references Students
   term_id tinyint not null
        references Terms
   average tinyiny,
   check (averge between 0 and 20),
   unique (student_id, term_id)
)

ستونی به نام identity وظیفه PK را به عهده می‌گیره. و از نوع identity هم هست.
دو ستون کد دانشجو و کد معدل کلید‌های خارجی هستند. و ترکیب این دو ستون برای حفظ یکپارچگی و جامعیت داده‌ها منحصربفرد نظر گرفته شدن.
یک قید هم برای معدل گذاشته شده که معدل غیر متعارف در آن درج نشه.

به سناریوی زیر توجه کنید:
فرض کنید میخواهید بر اساس کد دانشجو و یک ترم معین در جدول برای بدست آوردم معدل جستجو داشته باشید. خب لازمه که بر اساس آن دو ستون جستجو داشته باشید نه آن ستونی که به عنوان PK در نظر گرفته شده. پس محتویات ستون identity کاملا مصنوعی و غیر طبیعی بوده و بطور مستقیم قابل استفاده نیست.

البته لازم به ذکر که عموما کلید اولیه همزمان unique clustered index نیز در نظر گرفته میشه. اگر داده‌های این ستون بطور متوالی و پشت سر هم در جدول درج نشن باعث ایجاد fragmentation میشه. و لازمه که ایندکس rebuild بشه.
و اگر کلید اولیه ترکیبی باشه کار در ارتباطات کمی دشوار میشه چون نیاز به کلیدهای خارجی ترکیبی نیز هست.
در join‌ها نیز چون پیوند بر اساس کلید اولیه و کلیدخارجی هست، هر چه کلید اولیه سبک‌تر باشه (حچم کمتری داشته باشه و از نوعی باشه که سریع‌تر توسط پردازنده پردازش بشه) سرعت پردازش نیز طبیعتا افزایش پیدا میکنه.

‫۱۱ سال و ۸ ماه قبل، سه‌شنبه ۱۷ بهمن ۱۳۹۱، ساعت ۱۹:۰۰
من دقیقا متوجه نشدم نتیجه مورد نظر شما چیست.
آیا نتیجه مورد نظر شما به صورت الحاق یافته (concatenated)  هست یا نه؟
در هر صورت باید یکی از دو query زیر نتیجه مورد نظر شما را تولید کند.
declare @t table
(id char(1) primary key,
parent char(1));
 
insert @t values
('A',null),                                   --Level 1
('B', 'A'), ('C', 'A'),                       --Level 2
('D', 'B'), ('E', 'B'),('R','B'), ('F', 'C'), --Level 3
('G', 'D'),                                   --Level 4
('H', 'G'), ('I', 'G');                       --Level 5

;with cte as
(
select id, rnk=0, 
       concats = cast(id as varchar(10))
from @t
where parent is null
 
union all
 
select t.id, rnk+1,
       cast(cte.concats + t.id as varchar(10))
from cte join @t t
on cte.id = t.parent
)
select * from cte
/*
id   rnk         concats
---- ----------- ----------
A    0           A
B    1           AB
C    1           AC
F    2           ACF
D    2           ABD
E    2           ABE
R    2           ABR
G    3           ABDG
H    4           ABDGH
I    4           ABDGI
*/
;with cte as
(
select id, rnk=0, 
       concats = cast(id as varchar(10))
from @t
where parent is null
 
union all
 
select t.id, rnk+1,
       cast(cte.concats + t.id as varchar(10))
from cte join @t t
on cte.id = t.parent
)
select stuff(d.list,1,1,'') as concats
from (select ','+concats
      from cte
  for xml path(''))d(list)
/*
concats
----------------------------------------
A,AB,AC,ACF,ABD,ABE,ABR,ABDG,ABDGH,ABDGI
*/

موفق باشید
‫۱۱ سال و ۸ ماه قبل، شنبه ۱۴ بهمن ۱۳۹۱، ساعت ۱۲:۲۴
موضوعی که شما مطرح می‌کنید خارج از بحث مطرح شده است.
من تصمیم نداشتم که یک محیط عملیاتی را پیاده سازی کنم. تنها مثال هایی برای درک بهتر موضوع آوردم.
بله میانگین یک خاصیت آماری است، اما ما می‌توانیم برای سرعت بخشیدن به query هایمان برای بدست آوردن معدل، آن را بصورت فیزیکی ذخیره داشته باشیم. چون معدل بعد از ثابت و تعیین شدن دیگر تغییر نخواهد کرد.

‫۱۱ سال و ۸ ماه قبل، شنبه ۱۴ بهمن ۱۳۹۱، ساعت ۰۰:۲۸
این دو جدول آخر به شکل سوم نرمال هستند. یعنی شرط نرمال سوم را نیز محقق کرده اند. در مطلب بعدی یک مثال از جدولی خواهم آورد که به شکل دوم نرمال بوده ولی به شکل سوم نرمال نباشد.
‫۱۱ سال و ۸ ماه قبل، دوشنبه ۹ بهمن ۱۳۹۱، ساعت ۱۵:۴۵
این یک روش معمولی و رایج هست که همگان دارن ازش استفاده می‌کنند. بعید می‌دانم که در عملکرد مشکلی داشته باشد.
اگر فرضا سرعت اجرای query اتان پایین بود یک اندیس (index) روی ستون "کد والد" تعریف کنید
به این شکل
create nonclustered index ix_parent on table_name (parent_id) 


‫۱۱ سال و ۸ ماه قبل، دوشنبه ۹ بهمن ۱۳۹۱، ساعت ۰۱:۲۶
سلام،
برای این منظور باید از recursive cte کمک گرفت.
فرض کنید درختی به شکل زیر داریم:
/*         A
         /   \
        B     C
        |    /|\
        D   E F G
        |
        H
        
*/

و هدف بدست آوردن تمام زیر شاخته‌های گره A است.
ابتدا باید تمام گره هایی که مقدار گره پدرشان برابر با A است را بدست بیاریم یعنی گره‌های B و C
حالا باید تمام گره هایی که گره پدرشان B و یا C است را بدست بیاریم یعنی گره‌های D E F G
و در مرحله بعد باید تمام گره هایی را بدست بیاریم که گره پدرشان برابر با یکی از مقادیر بدست آمده در مرحله قبل (یعنی D E F G) یعنی H

این الگوریتم را توسط Recursive CTE پیاده میکنیم:
declare @t table
(
   id char(1) primary key not null,
   pid char(1) null --references @t
);

insert @t
values ('A', null), ('B','A'),('C','A'),
('D','B'), ('H','D'),('E','C'),('F','C'),('G','C');

with cte as 
(
select id
from @t 
where pid = 'A'

union all

select t.id
from cte c
join @t t
on t.pid = c.id
)
select * from cte

موفق باشید