2012-12-21 16 views
35

शीर्षक यह सब कहता है, मैं SQL सर्वर में किसी खंड में विंडो किए गए फ़ंक्शन का उपयोग क्यों नहीं कर सकता?क्यों खंडों में कोई खिड़कीदार कार्य नहीं है?

इस क्वेरी सही समझ में आता है:

select id, sales_person_id, product_type, product_id, sale_amount 
from Sales_Log 
where 1 = row_number() over(partition by sales_person_id, product_type, product_id order by sale_amount desc) 

लेकिन यह काम नहीं करता। क्या सीटीई/सबक्वायरी से बेहतर तरीका है?

संपादित

क्या अपने इस लायक एक CTE के साथ क्वेरी है:

with Best_Sales as (
    select id, sales_person_id, product_type, product_id, sale_amount, row_number() over (partition by sales_person_id, product_type, product_id order by sales_amount desc) rank 
    from Sales_log 
) 
select id, sales_person_id, product_type, product_id, sale_amount 
from Best_Sales 
where rank = 1 

संपादित

एक सबक्वेरी के साथ दिखा जवाब के लिए +1, लेकिन वास्तव में मैं मैं खंडों में खिड़की के कार्यों का उपयोग करने में सक्षम नहीं होने के पीछे तर्क की तलाश कर रहा हूं।

+1

घुमावदार कार्य गैर-संबंधपरक परत का हिस्सा हैं (क्योंकि संबंध सिद्धांत आदेशित डेटा से निपटता नहीं है); इस प्रकार उनका मूल्यांकन अन्य सभी के बाद किया जाता है। प्रश्न का उत्तर देने के लिए –

उत्तर

40

वास्तव में मैं में विंडोइंग कार्यों का उपयोग जहां खंड में सक्षम नहीं होने के पीछे तर्क की तलाश में हूँ।

कारण यह है कि WHERE खंड में उन्हें अनुमति नहीं है क्योंकि यह अस्पष्टता पैदा करेगा। High-Performance T-SQL Using Window Functions से पीजी इटज़िक बेन गण का उदाहरण चोरी (पी।25)

मान लीजिए कि आपकी मेज

CREATE TABLE T1 
(
col1 CHAR(1) PRIMARY KEY 
) 

INSERT INTO T1 VALUES('A'),('B'),('C'),('D'),('E'),('F') 

और आपकी क्वेरी था

SELECT col1 
FROM T1 
WHERE ROW_NUMBER() OVER (ORDER BY col1) <= 3 
AND col1 > 'B' 

क्या सही परिणाम हो सकता है? क्या आप उम्मीद करेंगे कि col1 > 'B' पंक्ति संख्या संख्या से पहले या उसके बाद अनुमानित है?

ROW_NUMBER परिणाम शेष के बाद सभी WHERE/HAVING खंड के साथ पेश किया गया है सेट पर SELECT के समय में मूल्यांकन किया जाता है।

+0

धन्यवाद, यह मुझे पागल कर रहा था, मैं इसे समझ नहीं पाया। – Crisfole

+0

क्या यही कारण है कि उन्हें ['GROUP BY' खंड] में अनुमति नहीं है (http://stackoverflow.com/questions/14111321/windowed-functions-can-only-appear-in-the-select-or -order-दर-खंड) ?? –

+0

@MahmoudGamal समूह में संचालित विंडो को चयन में उपयोग की जाने वाली खिड़की से अलग होने की आवश्यकता होगी क्योंकि समूहकरण और होने के बाद परिणामस्वरूप है। मान लीजिए कि आप समूह के सामने तार्किक रूप से मौजूद पंक्तियों के रूप में इसे परिभाषित कर सकते हैं, लेकिन मुझे लगता है कि बस मुझे लगता है कि यह काफी उलझन में होगा। –

9

CTE के लिए कोई जरूरत नहीं है, बस एक सबक्वेरी में विंडोइंग समारोह का उपयोग करें:

select id, sales_person_id, product_type, product_id, sale_amount 
from 
(
    select id, sales_person_id, product_type, product_id, sale_amount, 
    row_number() over(partition by sales_person_id, product_type, product_id order by sale_amount desc) rn 
    from Sales_Log 
) sl 
where rn = 1 

संपादित करें, जवाब देने के लिए मेरी टिप्पणी घूम रहा है।

विंडिंग फ़ंक्शंस तब तक नहीं किए जाते जब तक डेटा वास्तव में चयनित नहीं होता है जो WHERE खंड के बाद होता है। इसलिए यदि आप को WHERE खंड में उपयोग करने का प्रयास करते हैं तो मान अभी तक असाइन नहीं किया गया है।

+0

+1, लेकिन जो कुछ मैं ढूंढ रहा था वह नहीं ...क्या मैं इस बार सही ढंग से पूछूंगा? – Crisfole

+2

@ क्रिस्टोफरफॉफल मेरी समझ के आधार पर 'row_number' को असाइन नहीं किया जाता है जब तक कि रिकॉर्ड्स का चयन नहीं किया जाता है, इसलिए आप इसे' WHERE' खंड में नहीं रख सकते हैं क्योंकि मान अभी तक मौजूद नहीं है। – Taryn

+0

आह ... धन्यवाद। समझ में आता है, लेकिन यह अभी भी एक दयालु है। – Crisfole

3

आप आवश्यक रूप से एक CTE उपयोग करने की आवश्यकता नहीं है, आप() परिणाम ROW_NUMBER उपयोग करने के बाद सेट क्वेरी कर सकता है

select row, id, sales_person_id, product_type, product_id, sale_amount 
from (
    select 
     row_number() over(partition by sales_person_id, 
      product_type, product_id order by sale_amount desc) AS row, 
     id, sales_person_id, product_type, product_id, sale_amount 
    from Sales_Log 
    ) a 
where row = 1 
+0

+1 प्रश्न का उत्तर देने के लिए +1, लेकिन जो कुछ मैं ढूंढ रहा था वह नहीं ... क्या मैं इस बार सही ढंग से पूछूंगा? – Crisfole

1

हाँ दुर्भाग्य से जब आप कर एक विंडोड समारोह एसक्यूएल आप भले ही पर गुस्सा हो जाता है अपने जहां भविष्यवाणी वैध है। आप अपने चयन कथन में मूल्य रखने के लिए एक सीटीई या नेस्टेड चयन करते हैं, फिर बाद में उस मूल्य के साथ अपने सीटीई या नेस्टेड चयन का संदर्भ लें। सरल उदाहरण जो आत्म व्याख्यात्मक होना चाहिए। यदि आप बड़े डेटा सेट करने पर कुछ प्रदर्शन समस्या के लिए वास्तव में नफरत करते हैं तो आप हमेशा temp तालिका या तालिका चर में जा सकते हैं।

declare @Person table (PersonID int identity, PersonName varchar(8)); 

insert into @Person values ('Brett'),('John'); 

declare @Orders table (OrderID int identity, PersonID int, OrderName varchar(8)); 

insert into @Orders values (1, 'Hat'),(1,'Shirt'),(1, 'Shoes'),(2,'Shirt'),(2, 'Shoes'); 

--Select 
-- p.PersonName 
--, o.OrderName 
--, row_number() over(partition by o.PersonID order by o.OrderID) 
--from @Person p 
-- join @Orders o on p.PersonID = o.PersonID 
--where row_number() over(partition by o.PersonID order by o.orderID) = 2 

-- yields: 
--Msg 4108, Level 15, State 1, Line 15 
--Windowed functions can only appear in the SELECT or ORDER BY clauses. 
; 

with a as 
    (
    Select 
    p.PersonName 
, o.OrderName 
, row_number() over(partition by o.PersonID order by o.OrderID) as rnk 
from @Person p 
    join @Orders o on p.PersonID = o.PersonID 
    ) 
select * 
from a 
where rnk >= 2 -- only orders after the first one. 
1

अंत में, वहाँ एक सहसंबद्ध सबक्वेरी के साथ पुराने जमाने, पूर्व SQL सर्वर 2005 तरीका है,:

select * 
from Sales_Log sl 
where sl.id = (
    Select Top 1 id 
    from Sales_Log sl2 
    where sales_person_id = sl.sales_person_id 
     and product_type = sl.product_type 
     and product_id = sl.product_id 
    order by sale_amount desc 
) 

मैं केवल पूर्णता के लिए आप इस दे,।

2

यह सब कुछ पहले कहा जाता all-at-once operation

"सभी-पर-एक बार संचालन" का अर्थ है कि एक ही तार्किक क्वेरी प्रक्रिया चरण में सभी भाव एक ही समय में तार्किक मूल्यांकन किया जाता है।

और खिड़की कार्य पर महान अध्याय प्रभाव:

मान लीजिए आपके पास:

CREATE TABLE #Test (Id INT) ; 

INSERT INTO #Test VALUES (1001), (1002) ; 

SELECT Id 
FROM #Test 
WHERE Id = 1002 
    AND ROW_NUMBER() OVER(ORDER BY Id) = 1; 

सभी में-एक बार आपरेशन हमें इन दोनों स्थितियों में एक ही पर तार्किक का मूल्यांकन बता इस समय पर। इसलिए, SQL सर्वर अनुमानित निष्पादन योजना के आधार पर, मनमानी क्रम में WHERE खंड में स्थितियों का मूल्यांकन कर सकता है। तो यहां मुख्य प्रश्न यह है कि किस स्थिति का मूल्यांकन करता है।

केस 1:

If (Id = 1002) is first, then if (ROW_NUMBER() OVER(ORDER BY Id) = 1)

परिणाम: 1002

केस 2:

If (ROW_NUMBER() OVER(ORDER BY Id) = 1), then check if (Id = 1002)

परिणाम: खाली

तो हमारे पास एक विरोधाभास है।

यह उदाहरण दिखाता है कि हम WHERE खंड में विंडो फ़ंक्शन का उपयोग क्यों नहीं कर सकते हैं। आप इसके बारे में और सोच सकते हैं कि विंडो फ़ंक्शंस को केवल में उपयोग करने की अनुमति है और खंडों द्वारा ऑर्डर करें!