पहली पंक्ति में कैसे शामिल हों


773

मैं एक ठोस, लेकिन काल्पनिक का उपयोग करूँगा।

प्रत्येक आदेश में आम तौर पर केवल एक पंक्ति वस्तु होती है :

आदेश:

OrderGUID   OrderNumber
=========   ============
{FFB2...}   STL-7442-1      
{3EC6...}   MPT-9931-8A

लाइन आइटम:

LineItemGUID   Order ID Quantity   Description
============   ======== ========   =================================
{098FBE3...}   1        7          prefabulated amulite
{1609B09...}   2        32         spurving bearing

लेकिन कभी-कभी दो लाइन आइटम के साथ एक आदेश होगा:

LineItemID   Order ID    Quantity   Description
==========   ========    ========   =================================
{A58A1...}   6,784,329   5          pentametric fan
{0E9BC...}   6,784,329   5          differential girdlespring 

आम तौर पर उपयोगकर्ता को आदेश दिखाते समय:

SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
    INNER JOIN LineItems 
    ON Orders.OrderID = LineItems.OrderID

मैं आदेश पर एकल आइटम दिखाना चाहता हूं। लेकिन इस सामयिक आदेश दो आइटम वाले (या अधिक) के साथ, आदेश होता दिखाई जा दोहराया गया :

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         spurving bearing
KSG-0619-81   5          panametric fan
KSG-0619-81   5          differential girdlespring

क्या मैं वास्तव में चाहता हूँ कि SQL सर्वर सिर्फ एक है , क्योंकि यह काफी अच्छा होगा :

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         differential girdlespring
KSG-0619-81   5          panametric fan

यदि मुझे रोमांच मिलता है, तो मैं उपयोगकर्ता को यह दिखाने के लिए एक दीर्घवृत्त दिखा सकता हूं कि एक से अधिक हैं:

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         differential girdlespring
KSG-0619-81   5          panametric fan, ...

तो सवाल यह है कि या तो कैसे करें

  • "डुप्लिकेट" पंक्तियों को समाप्त करें
  • केवल एक पंक्ति में शामिल हों, दोहराव से बचने के लिए

पहला प्रयास

मेरा पहला भोला प्रयास केवल " TOP 1 " पंक्ति वस्तुओं में शामिल होना था:

SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
    INNER JOIN (
       SELECT TOP 1 LineItems.Quantity, LineItems.Description
       FROM LineItems
       WHERE LineItems.OrderID = Orders.OrderID) LineItems2
    ON 1=1

लेकिन यह त्रुटि देता है:

स्तंभ या उपसर्ग 'आदेश' क्वेरी में प्रयुक्त
तालिका नाम या उपनाम नाम से मेल नहीं खाता

संभवत: क्योंकि आंतरिक चयन बाहरी तालिका नहीं देखता है।


3
क्या आप उपयोग नहीं कर सकते group by?
दरियाश जाफरी

2
मुझे लगता है कि (और अगर मैं गलत हूं तो मुझे सुधारें) आपको group byअन्य सभी स्तंभों को सूचीबद्ध करने की आवश्यकता होगी, जहां आपको डुप्लिकेट नहीं चाहिए। स्रोत
जोशुआ नेल्सन

जवाबों:


1212
SELECT   Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM     Orders
JOIN     LineItems
ON       LineItems.LineItemGUID =
         (
         SELECT  TOP 1 LineItemGUID 
         FROM    LineItems
         WHERE   OrderID = Orders.OrderID
         )

SQL Server 2005 और इसके बाद के संस्करण में, आप इसके INNER JOINसाथ प्रतिस्थापित कर सकते हैं CROSS APPLY:

SELECT  Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM    Orders
CROSS APPLY
        (
        SELECT  TOP 1 LineItems.Quantity, LineItems.Description
        FROM    LineItems
        WHERE   LineItems.OrderID = Orders.OrderID
        ) LineItems2

कृपया ध्यान दें कि इसके TOP 1बिना ORDER BYनियतात्मक नहीं है: यह प्रश्न आपको प्रति आदेश आपको एक पंक्ति वस्तु मिलेगा, लेकिन यह परिभाषित नहीं है कि यह कौन सा होगा।

क्वेरी के कई इनवोकेशन आपको एक ही ऑर्डर के लिए अलग-अलग लाइन आइटम दे सकते हैं, भले ही अंतर्निहित परिवर्तन नहीं हुआ हो।

यदि आप निर्धारक आदेश चाहते हैं, तो आपको ORDER BYअंतर क्वेरी में एक खंड जोड़ना चाहिए ।


3
उत्कृष्ट, वह काम करता है; व्युत्पन्न तालिका खंड से शीर्ष 1 को खंड में शामिल होने के लिए ले जाना।
इयान बॉयड

107
और "OUTER JOIN" के बराबर "OUTER APPLY" होगा
Alex

9
कैसे के बारे में LEFT OUTER JOIN?
एलेक्स नोलस्को

8
यदि आप इसमें सम्मिलित होते हैं तो कंपाउंड कुंजी / एकाधिक कॉलम के माध्यम से कैसे करते हैं?
ब्रेट रायन

7
CROSS APPLYइसके बजाय INNER JOINऔर OUTER APPLYइसके बजाय LEFT JOIN(जैसा ही LEFT OUTER JOIN)।
12

117

मुझे पता है कि इस सवाल का जवाब कुछ समय पहले दिया गया था, लेकिन बड़े डेटा सेट के साथ काम करते समय, नेस्टेड क्वेरी महंगा हो सकता है। यहां एक अलग समाधान है जहां नेस्टेड क्वेरी को केवल एक बार चलाया जाएगा, बजाय प्रत्येक पंक्ति के लिए वापस लौटे।

SELECT 
  Orders.OrderNumber,
  LineItems.Quantity, 
  LineItems.Description
FROM 
  Orders
  INNER JOIN (
    SELECT
      Orders.OrderNumber,
      Max(LineItem.LineItemID) AS LineItemID
    FROM
      Orders INNER JOIN LineItems
      ON Orders.OrderNumber = LineItems.OrderNumber
    GROUP BY Orders.OrderNumber
  ) AS Items ON Orders.OrderNumber = Items.OrderNumber
  INNER JOIN LineItems 
  ON Items.LineItemID = LineItems.LineItemID

2
यदि आपका 'LineItemId' कॉलम ठीक से अनुक्रमित नहीं किया गया है, तो यह बहुत तेज़ है। स्वीकृत उत्तर की तुलना में।
GER

3
लेकिन आप यह कैसे करेंगे यदि मैक्स आप के लिए उपयोग करने योग्य नहीं है क्योंकि आपको उस कॉलम से अलग ऑर्डर करना होगा जिसे आप वापस करना चाहते हैं?
निकोग

2
आप जो भी चाहें व्युत्पन्न तालिका का आदेश दे सकते हैं और MySQL में SQL 1 या शीर्ष 1 में शीर्ष 1 का उपयोग कर सकते हैं
stifin

28

तुम यह कर सकते थे:

SELECT 
  Orders.OrderNumber, 
  LineItems.Quantity, 
  LineItems.Description
FROM 
  Orders INNER JOIN LineItems 
  ON Orders.OrderID = LineItems.OrderID
WHERE
  LineItems.LineItemID = (
    SELECT MIN(LineItemID) 
    FROM   LineItems
    WHERE  OrderID = Orders.OrderID
  )

इसके लिए एक इंडेक्स (या प्राथमिक कुंजी) की आवश्यकता होती है LineItems.LineItemIDऔर एक इंडेक्स पर LineItems.OrderIDया यह धीमा होगा।


2
यह काम नहीं करता है अगर एक आदेश में कोई LineItems नहीं है। उप-अभिव्यक्ति तब LineItems.LineItemID = nullपरिणाम से पूरी तरह से बाईं इकाई के आदेशों का मूल्यांकन और हटाती है।
leo

6
वह भी आंतरिक जुड़ाव का प्रभाव है, इसलिए ... हाँ।
तोमलक

1
समाधान जो कि लेफ्टिनेंट ऑर्टिन के लिए अनुकूलित किया जा सकता है: stackoverflow.com/a/20576200/510583
leo

3
@ येलो हां, लेकिन ओपी ने एक आंतरिक रूप से खुद को शामिल किया, इसलिए मुझे आपकी आपत्ति समझ में नहीं आई।
तोमलक

27

@Quassnoi का उत्तर अच्छा है, कुछ मामलों में (विशेषकर यदि बाहरी तालिका बड़ी है), एक अधिक कुशल क्वेरी विंडो कार्यों का उपयोग करने के साथ हो सकती है, जैसे:

SELECT  Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM    Orders
LEFT JOIN 
        (
        SELECT  LineItems.Quantity, LineItems.Description, OrderId, ROW_NUMBER()
                OVER (PARTITION BY OrderId ORDER BY (SELECT NULL)) AS RowNum
        FROM    LineItems

        ) LineItems2 ON LineItems2.OrderId = Orders.OrderID And RowNum = 1

कभी-कभी आपको बस यह परखना होता है कि कौन सी क्वेरी बेहतर प्रदर्शन देती है।


3
यह एकमात्र ऐसा उत्तर है जो मैंने पाया कि एक वास्तविक "लेफ्ट" जुड़ता है, जिसका अर्थ है कि यह कोई और रेखा नहीं जोड़ता है तो "लेफ्ट" तालिका में है। आपको बस उपशम में डालने और "जहां रोव्नम अशक्त नहीं है" जोड़ने की जरूरत है
user890332

1
इससे सहमत होना सबसे अच्छा उपाय है। यह समाधान आपको उस तालिका में एक विशिष्ट आईडी रखने की भी आवश्यकता नहीं है जिसमें आप शामिल हो रहे हैं, और शीर्ष मतदान जवाब की तुलना में बहुत तेज़ है। आप इस बात के लिए भी मानदंड जोड़ सकते हैं कि आप किस पंक्ति में लौटना पसंद करते हैं, बजाय एक यादृच्छिक पंक्ति के, उपनगर में एक ORDER BY खंड का उपयोग करके।
ज्योफ ग्रिज्डल्ड

यह एक अच्छा उपाय है। कृपया ध्यान दें: अपनी स्थिति के लिए उपयोग करते समय, बहुत सावधान रहें कि आप किस तरह से PART BY (आमतौर पर आप शायद वहां कुछ ID कॉलम चाहते हैं) और ORDER BY (जो कि ज्यादातर कुछ भी किया जा सकता है, इस पर निर्भर करता है कि आप किस पंक्ति को रखना चाहते हैं, जैसे। DateCreated desc कुछ तालिकाओं के लिए एक विकल्प होगा, लेकिन यह बहुत सारी चीजों पर निर्भर करेगा)
जोसेफडोगी

14

, आम तालिका अभिव्यक्ति का उपयोग कर एक और aproach:

with firstOnly as (
    select Orders.OrderNumber, LineItems.Quantity, LineItems.Description, ROW_NUMBER() over (partiton by Orders.OrderID order by Orders.OrderID) lp
    FROM Orders
        join LineItems on Orders.OrderID = LineItems.OrderID
) select *
  from firstOnly
  where lp = 1

या, अंत में शायद आप सभी पंक्तियों को मिला कर दिखाना चाहेंगे?

अल्पविराम से अलग संस्करण यहां:

  select *
  from Orders o
    cross apply (
        select CAST((select l.Description + ','
        from LineItems l
        where l.OrderID = s.OrderID
        for xml path('')) as nvarchar(max)) l
    ) lines

13

SQL सर्वर 2012 और उसके बाद से मुझे लगता है कि यह चाल चलेगा:

SELECT DISTINCT
    o.OrderNumber ,
    FIRST_VALUE(li.Quantity) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Quantity ,
    FIRST_VALUE(li.Description) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Description
FROM    Orders AS o
    INNER JOIN LineItems AS li ON o.OrderID = li.OrderID

2
सबसे अच्छा जवाब अगर आप मुझसे पूछें।
थॉमस

11

संबंधित उप-प्रश्न उप प्रश्न हैं जो बाहरी क्वेरी पर निर्भर करते हैं। यह SQL में लूप के लिए जैसा है। उप-क्वेरी बाहरी पंक्ति में प्रत्येक पंक्ति के लिए एक बार चलेगी:

select * from users join widgets on widgets.id = (
    select id from widgets
    where widgets.user_id = users.id
    order by created_at desc
    limit 1
)

5

संपादित करें: कोई बात नहीं, क्वासोई का बेहतर जवाब है।

SQL2K के लिए, कुछ इस तरह से:

SELECT 
  Orders.OrderNumber
, LineItems.Quantity
, LineItems.Description
FROM (  
  SELECT 
    Orders.OrderID
  , Orders.OrderNumber
  , FirstLineItemID = (
      SELECT TOP 1 LineItemID
      FROM LineItems
      WHERE LineItems.OrderID = Orders.OrderID
      ORDER BY LineItemID -- or whatever else
      )
  FROM Orders
  ) Orders
JOIN LineItems 
  ON LineItems.OrderID = Orders.OrderID 
 AND LineItems.LineItemID = Orders.FirstLineItemID

4

इस क्वेरी को चलाने का मेरा पसंदीदा तरीका मौजूद नहीं है। मेरा मानना ​​है कि इस तरह की क्वेरी को चलाने का यह सबसे कारगर तरीका है:

select o.OrderNumber,
       li.Quantity,
       li.Description
from Orders as o
inner join LineItems as li
on li.OrderID = o.OrderID
where not exists (
    select 1
    from LineItems as li_later
    where li_later.OrderID = o.OrderID
    and li_later.LineItemGUID > li.LineItemGUID
    )

लेकिन मैंने इस पद्धति का परीक्षण यहां सुझाए गए अन्य तरीकों के खिलाफ नहीं किया है।


2

क्रॉस की कोशिश की, अच्छी तरह से काम करता है, लेकिन थोड़ा अधिक समय लगता है। समायोजित लाइन कॉलम में अधिकतम और जोड़ा गया समूह है जो गति बनाए रखता है और अतिरिक्त रिकॉर्ड को गिरा देता है।

यहाँ समायोजित क्वेरी है:

SELECT Orders.OrderNumber, max(LineItems.Quantity), max(LineItems.Description)
FROM Orders
    INNER JOIN LineItems 
    ON Orders.OrderID = LineItems.OrderID
Group by Orders.OrderNumber

10
लेकिन दो स्तंभों पर अलग-अलग अधिकतम होने का मतलब है कि मात्रा विवरण से संबंधित नहीं हो सकती है। यदि ऑर्डर 2 विजेट्स और 10 गैजेट्स थे, तो क्वेरी 10 विजेट्स वापस कर देगी।
ब्रायनॉर्का

1

इसे इस्तेमाल करे

SELECT
   Orders.OrderNumber,
   LineItems.Quantity, 
   LineItems.Description
FROM Orders
   INNER JOIN (
      SELECT
         Orders.OrderNumber,
         Max(LineItem.LineItemID) AS LineItemID
       FROM Orders 
          INNER JOIN LineItems
          ON Orders.OrderNumber = LineItems.OrderNumber
       GROUP BY Orders.OrderNumber
   ) AS Items ON Orders.OrderNumber = Items.OrderNumber
   INNER JOIN LineItems 
   ON Items.LineItemID = LineItems.LineItemID

2
कृपया यह समझाने पर विचार करें कि ओपी की समस्या को हल करने के लिए आपकी क्वेरी क्या करती है
Simas Joneliunas
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.