2009-04-21 9 views
26

मेरे पास 2 टेबल हैं - एक खाता तालिका और उपयोगकर्ता तालिका। प्रत्येक खाते में एकाधिक उपयोगकर्ता हो सकते हैं। मेरे पास एक परिदृश्य है जहां मैं इन दो तालिकाओं के खिलाफ एक क्वेरी/निष्पादित करना चाहता हूं, लेकिन मुझे सभी खाता डेटा (खाता। *) और केवल पहले उपयोगकर्ता डेटा (विशेष रूप से उनका नाम) सेट करना है।कुल समूह फ़ंक्शन प्रत्येक समूह से केवल पहले को पकड़ने के लिए

मेरे समेकित समूह पर "न्यूनतम" या "अधिकतम" करने के बजाय, मैं "पहला" करना चाहता था। लेकिन जाहिर है, टीएसक्यूएल में कोई "पहला" कुल कार्य नहीं है।

इस प्रश्न को प्राप्त करने के बारे में कोई सुझाव कैसे है?

SELECT User.Name, Account.* FROM Account, User 
WHERE Account.ID = User.Account_ID 

लेकिन कैसे के बारे में मैं केवल उनके User.ID के आदेश के आधार पर उत्पाद से पहले उपयोगकर्ता हो रही हो गया हो सकता है: जाहिर है, यह खाता एक्स उपयोगकर्ता की कार्तीय उत्पाद प्राप्त करने के लिए आसान है?

+0

एसक्यूएल सर्वर बदतर है, क्योंकि यह कोई पहला है के लिए पहले उपयोगकर्ता रिटर्न के साथ शामिल हो। मैंने एक ठोस स्पष्टीकरण नहीं सुना है कि यह SQL सर्वर में क्यों मौजूद नहीं है। कभी-कभी इससे कोई फर्क नहीं पड़ता कि वे किस क्रम में हैं (यदि उनके पास किसी विशेष समूह के लिए कॉलम में समान मूल्य है) और कभी-कभी यह करता है (और उन्हें आदेश दिया जाता है)। किसी भी तरह से पहले() का उपयोग होगा। – micahhoover

उत्तर

22

बल्कि समूह की तुलना में, इस तरह इसके बारे में जाना ...

select 
    * 

from account a 

join (
    select 
     account_id, 
     row_number() over (order by account_id, id) - 
      rank() over (order by account_id) as row_num from user 
    ) first on first.account_id = a.id and first.row_num = 0 
+0

दिलचस्प, मुझे नहीं पता था कि आप पहले कुछ कर सकते हैं .row_num = 0 – Matt

+1

मैं देखता हूं कि आपने रैंक() का उपयोग किया है, फिर इसे Row_Number() से घटा दिया है, और 0 के लिए देखा है। मैं केवल Row_Number() (इसके साथ Account_ID द्वारा विभाजित) का उपयोग करता हूं और Row_Num = 1 पर फ़िल्टर किया जाता है। परिणाम वही होंगे (और शायद तकनीकी रूप से तेज़)। @AaronLS का उदाहरण देखें: http://stackoverflow.com/a/9220232/555798 – MikeTeeVee

+2

@ माइकटीवी सहमत हैं; यह एक बेहतर समाधान है, और यही वह होगा जो मैं आज उस समस्या को हल कर रहा था। –

1
SELECT (SELECT TOP 1 Name 
     FROM User 
     WHERE Account_ID = a.AccountID 
     ORDER BY UserID) [Name], 
     a.* 
FROM Account a 
+0

हालांकि, यह दृष्टिकोण प्रत्येक खाता पंक्ति के लिए एक और चयन कथन निष्पादित करेगा। यदि आपके पास 1000 खाते हैं, तो आपकी क्वेरी 1001 स्वतंत्र चयन कथन निष्पादित करेगी) –

+0

छोटी तालिकाओं के लिए कोई बड़ा सौदा नहीं है, लेकिन आपका समाधान बेहतर है :) –

0

एक एक त्वरित और गंदे एक ऐसा करने के लिए, यहाँ के तरीकों में से एक संख्या हैं।

Select (SELECT TOP 1 U.Name FROM Users U WHERE U.Account_ID = A.ID) AS "Name, 
    A.* 
FROM Account A 
0

"पहले" परिभाषित करें। जो आप पहले सोचते हैं वह एक संयोग है जिसे आम तौर पर क्लस्टर इंडेक्स ऑर्डर के साथ करना पड़ता है लेकिन इस पर भरोसा नहीं किया जाना चाहिए (आप इसे तोड़ने वाले उदाहरणों का विरोध कर सकते हैं)।

आप MAX() या MIN() का उपयोग न करने का अधिकार हैं। मोहक होने पर, परिदृश्य पर विचार करें जहां आप पहला नाम और अंतिम नाम अलग-अलग क्षेत्रों में हैं। आपको विभिन्न अभिलेखों से नाम मिल सकते हैं।

चूंकि ऐसा लगता है कि आपकी सभी वास्तव में देखभाल है कि आप प्रत्येक समूह के लिए बिल्कुल एक मनमाना रिकॉर्ड प्राप्त करते हैं, आप जो कर सकते हैं वह उस रिकॉर्ड के लिए केवल MIN या MAX आईडी फ़ील्ड है, और उसके बाद उस तालिका में क्वेरी में शामिल हों आईडी।

+0

में एक खंड का उपयोग करने का हल किया, उन्होंने पहले कहा कि उनके उपयोगकर्ता आईडी –

2

पहला और अंतिम एसक्यूएल सर्वर 2005 या 2008 में मौजूद नहीं है, लेकिन एसक्यूएल सर्वर 2012 में पहले_वैल्यू, Last_Value फ़ंक्शन है। मैंने एसक्यूएल सर्वर 2005 के लिए कुल प्रथम और अंतिम कार्यान्वयन करने की कोशिश की और बाधा आई कि SQL सर्वर एक निर्धारित क्रम में कुल की गणना की गारंटी देता है। (विशेषता SqlUserDefinedAggregateAttribute.IsInvariantToOrder प्रॉपर्टी देखें, जिसे लागू नहीं किया गया है।) ऐसा इसलिए हो सकता है क्योंकि क्वेरी विश्लेषक एकाधिक थ्रेड पर कुल की गणना निष्पादित करने का प्रयास करता है और परिणामों को गठबंधन करता है, जो निष्पादन को गति देता है, लेकिन किसी ऑर्डर की गारंटी नहीं देता है कौन से तत्व एकत्रित हैं।

+1

पर आधारित स्टैक ओवरफ़्लो में आपका स्वागत है! प्रतिलिपि पोस्ट करते समय सावधान रहें और बॉयलरप्लेट/वर्बैटिम कई प्रश्नों के उत्तर पेस्ट करें, इन्हें समुदाय द्वारा "स्पैमी" के रूप में फ़्लैग किया जाना चाहिए। यदि आप ऐसा कर रहे हैं तो आमतौर पर इसका मतलब है कि प्रश्न डुप्लिकेट हैं इसलिए उन्हें इसके बजाय ध्वजांकित करें। – Kev

6
Select * 
From Accounts a 
Left Join (
    Select u.*, 
    row_number() over (Partition By u.AccountKey Order By u.UserKey) as Ranking 
    From Users u 
) as UsersRanked 
    on UsersRanked.AccountKey = a.AccountKey and UsersRanked.Ranking = 1 

इसे विभाजन द्वारा विभाजन का उपयोग करके सरल बनाया जा सकता है। उपर्युक्त में, यदि किसी खाते में तीन उपयोगकर्ता हैं, तो सबक्वायरी संख्या उन्हें 1,2, और 3, और एक अलग खाताकी के लिए, यह numnbering को रीसेट कर देगा। इसका मतलब प्रत्येक अद्वितीय खाताकी के लिए है, हमेशा 1, और संभावित 2,3,4, आदि

इस प्रकार आप प्रत्येक समूह से पहले को पकड़ने के लिए रैंकिंग = 1 पर फ़िल्टर करते हैं।

यह आपको प्रति खाता एक पंक्ति देगा, और यदि उस खाते के लिए कम से कम एक उपयोगकर्ता है, तो यह आपको सबसे कम कुंजी वाला उपयोगकर्ता देगा (क्योंकि मैं बाएं शामिल होने का उपयोग करता हूं, आपको हमेशा एक खाता मिल जाएगा कोई उपयोगकर्ता मौजूद होने पर भी लिस्टिंग)। Order By u.UserKey को किसी अन्य फ़ील्ड के साथ बदलें यदि आप पसंद करते हैं कि पहले उपयोगकर्ता को वर्णानुक्रम या कुछ अन्य मानदंडों का चयन किया जाए।

9

मुझे पता है कि मेरा जवाब थोड़ा देर हो चुकी है, लेकिन इससे दूसरों की मदद मिल सकती है। प्राप्त करने के लिए एसक्यूएल सर्वर में एक सबसे पहले() और अंतिम() एक तरीका होता है, और यहाँ यह है:

Stuff(Min(Convert(Varchar, DATE_FIELD, 126) + Convert(Varchar, DESIRED_FIELD)), 1, 23, '') 

उपयोग मिन() पहले() और अंतिम के लिए मैक्स()() के लिए। DATE_FIELD वह तारीख होनी चाहिए जो निर्धारित करती है कि यह पहला या अंतिम रिकॉर्ड है या नहीं। DESIRED_FIELD वह फ़ील्ड है जिसे आप पहले या अंतिम मान चाहते हैं। क्या यह करता है:

  1. स्ट्रिंग के शुरू में ISO प्रारूप में तारीख जोड़ें (23 वर्ण लंबा)
  2. कि स्ट्रिंग
  3. को DESIRED_FIELD संलग्न है कि क्षेत्र के लिए न्यूनतम/अधिकतम मूल्य प्राप्त करें (के बाद से यह तारीख के साथ शुरू करते हैं, आप पहले या अंतिम रिकार्ड)
  4. सामग्री है कि स्ट्रिंग concatened पहले 23 अक्षर दूर करने के लिए (तारीख हिस्सा)

यहां आपको मिल जाएगा!

संपादित करें: मुझे पहले सूत्र के साथ समस्याएं मिलीं: जब DATE_FIELD में .000 मिलीसेकंड के रूप में है, तो SQL सर्वर किसी भी मिलीसेकंड के साथ स्ट्रिंग के रूप में दिनांक लौटाता है, इस प्रकार DESIRED_FIELD से पहले 4 वर्णों को हटा देता है। मैंने बस प्रारूप को "20" (मिलीसेकंड के बिना) में बदल दिया और यह सब बढ़िया काम करता है। केवल नकारात्मक पक्ष यह है कि यदि आपके पास दो फ़ील्ड हैं जो एक ही सेकंड में बनाए गए थे, तो सॉर्ट संभवतः गन्दा हो सकता है ... जिसमें आप कैसा प्रारूप के लिए "126" पर वापस जा सकते हैं।

Stuff(Max(Convert(Varchar, DATE_FIELD, 20) + Convert(Varchar, DESIRED_FIELD)), 1, 19, '') 

संपादित करें 2: मेरा मूल उद्देश्य अंतिम (या पहले) गैर पूर्ण पंक्ति को वापस करना था। मुझे पूछा गया कि आखिरी या पहली पंक्ति कैसे वापस करनी है, गीला होना शून्य है या नहीं। बस एक ISNULL को DESIRED_FIELD में जोड़ें। जब आप एक + ऑपरेटर के साथ दो तारों को जोड़ते हैं, जब उनमें से एक न्यूल होता है, तो परिणाम नल होता है। तो निम्न का उपयोग करें:

Stuff(Max(Convert(Varchar, DATE_FIELD, 20) + IsNull(Convert(Varchar, DESIRED_FIELD), '')), 1, 19, '') 
+0

मैंने शीर्ष 1 के साथ नेस्टेड चयन के बजाय न्यूनतम() का उपयोग करके एक महत्वपूर्ण प्रदर्शन हिट देखा। मुझे लगता है कि कारण यह है कि मिनट पूरे डेटा सेट को पुन: सक्रिय करता है, जहां शीर्ष 1 केवल पहले व्यक्ति को लेता है। –

3

डोमिनिक गौलेट से STUFF प्रतिक्रिया धीमी है। लेकिन, यदि आपका DATE_FIELD SMALLDATETIME है (DATETIME के ​​बजाए), तो आईएसओ 8601 लंबाई 23 की बजाय 1 9 होगी (क्योंकि SMALLDATETIME में कोई मिलीसेकंड नहीं है) - इसलिए STUFF पैरामीटर को तदनुसार समायोजित करें या STUFF फ़ंक्शन से वापसी मान गलत होगा (पहले चार अक्षर गायब)।

+0

टिप्पणी के लिए धन्यवाद! मैंने देखा कि कुछ हफ्ते पहले भी, मेरा जवाब अपडेट किया गया। यह तब भी होता है जब आपके डेटाटाइम में .000 मिलीसेकंड के रूप में होता है, वे केवल छीन दिए जाते हैं और आप पहले 4 अक्षर खो देते हैं। मैंने मिलीसेकंड को हमेशा काटने के लिए प्रारूप को 126 से 20 तक बदल दिया, अब यह बहुत अच्छा काम कर रहा है! –

2

आप बाहरी आवेदन का उपयोग कर सकते हैं, documentation देखें।

SELECT User1.Name, Account.* FROM Account 
OUTER APPLY 
    (SELECT TOP 1 Name 
    FROM [User] 
    WHERE Account.ID = [User].Account_ID 
    ORDER BY Name ASC) User1 
1

मैं सभी तरीकों, simpelest और इस लक्ष्य को हासिल करने के लिए सबसे तेजी से विधि बेंचमार्क गए बाहरी/पार का उपयोग करना है लागू

SELECT u.Name, Account.* FROM Account 
OUTER APPLY (SELECT TOP 1 * FROM User WHERE Account.ID = Account_ID) as u 

क्रॉस सिर्फ अंदरूनी शामिल हों तरह काम करता है लागू करते हैं और पंक्तियों जहां दोनों को हासिल करेगा टेबल संबंधित हैं, जबकि बाहरी आवेदन बाएं बाहरी जॉइन की तरह काम करता है और बाएं टेबल (खाता यहां) से सभी पंक्तियां प्राप्त करता है

+0

यह क्वेरी असंगत परिणाम दे सकती है। बिना एसओटी के शीर्ष 1 चुनें, क्वेरी के किसी भी मैच को वापस कर सकते हैं, यह एसक्यूएल सर्वर इंजन पर निर्भर करता है। और इस प्रकार ऐसा परिणाम "यादृच्छिक परिणाम" दे सकता है। – qub1n

0

(थोड़ा ऑफ-टॉपिक, लेकिन) मैं अक्सर अपवाद सारांश सूचीबद्ध करने के लिए कुल प्रश्न चलाता हूं, और फिर मैं चाहता हूं यह जानने के लिए कि ग्राहक एक परिणाम में क्यों है, इसलिए 2 अर्ध-यादृच्छिक नमूने देने के लिए MIN और MAX का उपयोग करें जिन्हें मैं विवरण में देख सकता हूं उदा।

SELECT Customer.Id, COUNT(*) AS ProblemCount 
     , MIN(Invoice.Id) AS MinInv, MAX(Invoice.Id) AS MaxInv 
FROM Customer 
INNER JOIN Invoice on Invoice.CustomerId = Customer.Id 
WHERE Invoice.SomethingHasGoneWrong=1 
GROUP BY Customer.Id 
0

बनाएँ और एक subselect 'FirstUser' है कि प्रत्येक खाते

SELECT User.Name, Account.* 
FROM Account, User, 
(select min(user.id) id,account_id from User group by user.account_id) as firstUser 
WHERE Account.ID = User.Account_ID 
and User.id = firstUser.id and Account.ID = firstUser.account_id 
संबंधित मुद्दे