2008-09-08 17 views
12

मेरे पास एक तालिका है जिसमें एमएस एसक्यूएल 2005 तालिका में कई अलग-अलग "चीजें" के लिए कीमतें हैं। प्रति दिन प्रति सैकड़ों रिकॉर्ड हैं और अलग-अलग चीजों को अलग-अलग समय पर मूल्य अपडेट मिलते हैं।नवीनतम मूल्य प्राप्त करने के लिए SQL क्वेरी

ID uniqueidentifier not null, 
ThingID int NOT NULL, 
PriceDateTime datetime NOT NULL, 
Price decimal(18,4) NOT NULL 

मुझे चीजों के समूह के लिए आज की नवीनतम कीमतें प्राप्त करने की आवश्यकता है। नीचे दी गई क्वेरी काम करती है लेकिन मुझे सैकड़ों पंक्तियां मिल रही हैं और मुझे उन्हें लूप करना होगा और केवल थिंगिड प्रति नवीनतम निकालना होगा। मैं कैसे कर सकता हूं (उदा। ग्रुप बाय के माध्यम से) कहता है कि मैं प्रति थिंगिड में नवीनतम एक चाहता हूं? या मुझे subqueries का उपयोग करना होगा?

SELECT * 
FROM Thing 
WHERE ThingID IN (1,2,3,4,5,6) 
    AND PriceDate > cast(convert(varchar(20), getdate(), 106) as DateTime) 

अद्यतन: जटिलता को छिपाने के प्रयास में मैं एक एक पूर्णांक के आईडी कॉलम डाल दिया। वास्तविक जीवन में यह GUID है (और अनुक्रमिक प्रकार नहीं)। मैंने अद्वितीय पहचानकर्ता का उपयोग करने के लिए ऊपर तालिका तालिका को अद्यतन किया है।

+0

@BlaM: दुर्भाग्यवश आईडी एक GUID है और एक Int नहीं है। (जिसे आप उस समय नहीं जानते थे)। माफ़ कीजिये। – Marius

उत्तर

20

मुझे लगता है कि अपनी मेज संरचना के साथ एकमात्र समाधान एक सबक्वेरी के साथ काम करने के लिए है:

SELECT * 
    FROM Thing 
    WHERE ID IN (SELECT max(ID) FROM Thing 
        WHERE ThingID IN (1,2,3,4) 
        GROUP BY ThingID) 

(देखते हुए उच्चतम आईडी भी नवीनतम कीमत का मतलब है)

हालांकि मैं सुझाव है कि आप एक "IsCurrent जोड़ने "कॉलम 0 है यदि यह नवीनतम कीमत या 1 नहीं है तो यह नवीनतम है। यह असंगत डेटा के संभावित जोखिम को जोड़ देगा, लेकिन तालिका पूरी होने पर यह पूरी प्रक्रिया को तेज करेगी (यदि यह एक सूचकांक में है)। तो फिर तुम सब करने की जरूरत के लिए ...

SELECT * 
    FROM Thing 
    WHERE ThingID IN (1,2,3,4) 
    AND IsCurrent = 1 

अद्यतन

ठीक है, मार्कस सवाल पता चलता है कि आईडी एक uniqueid, न कि एक पूर्णांक है अद्यतन है। इससे क्वेरी को और भी जटिल बना दिया जाता है।

SELECT T.* 
    FROM Thing T 
    JOIN (SELECT ThingID, max(PriceDateTime) 
      WHERE ThingID IN (1,2,3,4) 
      GROUP BY ThingID) X ON X.ThingID = T.ThingID 
           AND X.PriceDateTime = T.PriceDateTime 
    WHERE ThingID IN (1,2,3,4) 

मैं वास्तव में या तो एक "IsCurrent" स्तंभ का सुझाव देते हैं या उत्तर में पाया अन्य सुझाव के साथ जाने के लिए और का उपयोग करें "वर्तमान कीमत" तालिका और एक अलग "कीमत इतिहास" तालिका (जो अंततः होगा चाहते हैं सबसे तेज़, क्योंकि यह मूल्य तालिका को छोटा रखता है)।

(मुझे पता है कि तल पर ThingID अनावश्यक है। बस अगर यह के साथ या बिना कि "कहाँ"। सुनिश्चित नहीं हैं कि कौन-सा संस्करण तेजी से हो जाएगा के बाद अनुकूलक अपने काम किया है। तेजी से होता है की कोशिश)

+0

'जॉइन' क्वेरी बहुत अधिक है, जिसमें 'चयन' वाला एम्बेडेड है, जिसमें 'शामिल' एक अतिरिक्त चयन करता है, 'प्रत्येक प्रविष्टि के लिए कहां है')। क्या आप इसे इंगित करने के लिए अपने anwser संपादित कर सकते हैं? – skolima

+0

@skolima: आप कैसे सुझाव देंगे कि मैं subquery को शामिल होने के साथ प्रतिस्थापित करता हूं। मुझे नहीं लगता कि यह संभव है, क्योंकि मुझे "बीच में" एक समग्र कार्य की आवश्यकता है। – BlaM

+0

वैसे मुझे सबकुछ से छुटकारा पाने का कोई तरीका नहीं पता है। हालांकि, आपके "अद्यतन जटिल प्रतिक्रिया" में, सबक्वायरी एक बार निष्पादित हो जाती है। आपकी पहली क्वेरी में, सबकुरी को कई बार कहा जाता है क्योंकि 'थिंग' में आइटम हैं। कम से कम MySQL पर। – skolima

2

मैं निम्न सबक्वायरी की तरह कुछ कोशिश करेंगे और आपके डेटा संरचनाओं को बदलने के बारे में भूल जाओगे।

SELECT 
* 
FROM 
Thing 
WHERE 
(ThingID, PriceDateTime) IN 
(SELECT 
    ThingID, 
    max(PriceDateTime) 
    FROM 
    Thing 
    WHERE 
    ThingID IN (1,2,3,4) 
    GROUP BY 
    ThingID 
) 

संपादित ऊपर ANSI SQL है और अब मैं टी एसक्यूएल के लिए एक सबक्वेरी does not काम में एक से अधिक स्तंभ होने का अनुमान लगा रहा हूँ। Marius, मैं निम्नलिखित का परीक्षण नहीं कर सकता लेकिन कोशिश करो;

SELECT 
p.* 
FROM 
Thing p, 
(SELECT ThingID, max(PriceDateTime) FROM Thing WHERE ThingID IN (1,2,3,4) GROUP BY ThingID) m 
WHERE 
p.ThingId = m.ThingId 
and p.PriceDateTime = m.PriceDateTime 

दूसरा विकल्प एक स्ट्रिंग में तारीख को बदलने और आईडी के साथ संयोजित करने के लिए हो सकता है ताकि आपके पास केवल एक कॉलम हो। यद्यपि यह थोड़ा बुरा होगा।

+0

मार्क, मैंने कोशिश की लेकिन एसक्यूएल सर्वर शिकायत कहां से शिकायत करता है। मुझे नहीं पता था कि आप कहां कर सकते हैं "जहां (थिंगिड, प्राइसडेट टाइम) ..."? – Marius

1

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

जैसा कि मैंने कहा है, आपके एक्सेस मॉडल के आधार पर, यह एक विकल्प हो सकता है।

2

तो सबक्वेरी मार्ग बहुत धीमी गति से मैं एक ऑडिट लॉग के रूप में अपने मूल्य के अपडेट के इलाज और एक ThingPrice तालिका को बनाए रखने पर विचार करेंगे था - शायद कीमत अद्यतन मेज पर एक ट्रिगर के रूप में:

ThingID int not null, 
UpdateID int not null, 
PriceDateTime datetime not null, 
Price decimal(18,4) not null 

प्राथमिक कुंजी हैं बस थिंग आईडी हो और "अपडेट आईडी" आपकी मूल तालिका में "आईडी" है।

1

मैं अनन्य पहचानकर्ता को बाइनरी में परिवर्तित कर रहा हूं ताकि मुझे इसका अधिकतम MAX मिल सके। यह सुनिश्चित करें कि आप समान ThingIDs और PriceDateTimes के साथ कई रिकॉर्ड से डुप्लिकेट नहीं मिलेगा बनाना चाहिए:

SELECT * FROM Thing WHERE CONVERT(BINARY(16),Thing.ID) IN 
(
SELECT MAX(CONVERT(BINARY(16),Thing.ID)) 
    FROM Thing 
    INNER JOIN 
    (SELECT ThingID, MAX(PriceDateTime) LatestPriceDateTime FROM Thing 
    WHERE PriceDateTime >= CAST(FLOOR(CAST(GETDATE() AS FLOAT)) AS DATETIME) 
    GROUP BY ThingID) LatestPrices 
    ON Thing.ThingID = LatestPrices.ThingID 
    AND Thing.PriceDateTime = LatestPrices.LatestPriceDateTime 
GROUP BY Thing.ThingID, Thing.PriceDateTime 
) AND Thing.ThingID IN (1,2,3,4,5,6) 
1

के बाद से आईडी अनुक्रमिक नहीं है, मुझे लगता है आप ThingID और PriceDateTime पर तो केवल एक ही कीमत कर सकते हैं एक अद्वितीय सूचकांक किसी दिए गए आइटम के लिए सबसे हालिया हो।

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

SELECT * 
FROM Thing thi 
WHERE thi.ThingID IN (1,2,3,4,5,6) 
    AND thi.PriceDateTime = 
    (SELECT MAX(maxThi.PriceDateTime) 
     FROM Thing maxThi 
     WHERE maxThi.PriceDateTime >= CAST(CONVERT(varchar(20), GETDATE(), 106) AS DateTime) 
     AND maxThi.ThingID = thi.ThingID) 

ध्यान दें कि मैं बदल गया ">" "> =" के बाद से आप एक दिन

2

के शुरू में एक कीमत सही हो सकता था जब से तुम SQL सर्वर 2005 का उपयोग कर रहे हैं, तो आप नया करने के लिए उपयोग कर सकते हैं (क्रॉस | आउटटर) क्लॉज लागू करें। लागू खंड आप तालिका तालिका वाले फ़ंक्शन वाले तालिका में शामिल होने देते हैं।

समस्या को हल करने के लिए, पहले एक मेज महत्वपूर्ण समारोह एक विशिष्ट आईडी के लिए बात से शीर्ष एन पंक्तियों को पुनः प्राप्त करने को परिभाषित, तिथि का आदेश दिया:

CREATE FUNCTION dbo.fn_GetTopThings(@ThingID AS GUID, @n AS INT) 
    RETURNS TABLE 
AS 
RETURN 
    SELECT TOP(@n) * 
    FROM Things 
    WHERE ThingID= @ThingID 
    ORDER BY PriceDateTime DESC 
GO 

और उसके बाद में शीर्ष 1 रिकॉर्ड पुनः प्राप्त करने के कार्य का उपयोग एक प्रश्न:

SELECT * 
    FROM Thing t 
CROSS APPLY dbo.fn_GetTopThings(t.ThingID, 1) 
WHERE t.ThingID IN (1,2,3,4,5,6) 

जादू यहां लागू खंड द्वारा किया जाता है जो छोड़ परिणाम में प्रत्येक पंक्ति के लिए समारोह लागू होता है तो परिणाम समारोह से वापस लौटे सेट के साथ जुड़ जाता है सेट तो अंतिम परिणाम सेट retuns ।(नोट: लागू की तरह शामिल हो एक छोड़ दिया करने के लिए, का उपयोग outter लागू जो बाईं ओर से सभी पंक्तियों को देता है, क्रॉस रिटर्न लागू जबकि केवल पंक्तियों सही पक्ष में एक मैच है)

Blam: क्योंकि मैं कर सकते हैं ' टी अभी तक टिप्पणियां पोस्ट नहीं करें (कम रेप पॉइंट्स के कारण) मेरे अपने उत्तरों के लिए भी ^^, मैं संदेश के मुख्य भाग में जवाब दूंगा: - लागू होने वाले खंड, यदि यह तालिका मूल्यवान कार्यों का उपयोग करता है तो इसे आंतरिक रूप से एसक्यूएल द्वारा अनुकूलित किया जाता है सर्वर इस तरह से है कि यह बाएं परिणाम सेट में प्रत्येक पंक्ति के लिए फ़ंक्शन को कॉल नहीं करता है, बल्कि इसके बजाय आंतरिक एसक्यूएल को फ़ंक्शन से लेता है और शेष क्वेरी के साथ इसे शामिल खंड में परिवर्तित करता है, इसलिए प्रदर्शन बराबर है या यहां तक ​​कि बेहतर (अगर योजना को एसक्यूएल सर्वर द्वारा चुना जाता है और आगे की अनुकूलन की जा सकती है) उपक्विरी का उपयोग कर क्वेरी के प्रदर्शन की तुलना में), और मेरे व्यक्तिगत प्रयोग में खिलाडि़यों लागू होते हैं जब डेटाबेस समुचित रूप से सूचीबद्ध है कोई प्रदर्शन के मुद्दों है और तारीख तक (सबक्वेरी के साथ एक सामान्य प्रश्न की तरह ही इस तरह की स्थितियों में व्यवहार करती है) आँकड़े हैं

+0

मैंने मुख्य रूप से SQL Server 2000 के साथ काम किया है, इसलिए यह मेरे लिए एक नई अवधारणा है। तकनीकी रूप से, क्या यह सभी पंक्तियों के साथ एक अस्थायी तालिका बनाएगा और फिर अस्थायी तालिका पर "फ़ंक्शन" चलाएगा? यह मेरे "जॉइन" समाधान में गति से तुलना कैसे करता है? – BlaM

0

प्रयास करें इस (बशर्ते आप केवल नवीनतम कीमत, की जरूरत नहीं है पहचानकर्ता या उस कीमत के datetime)

SELECT ThingID, (SELECT TOP 1 Price FROM Thing WHERE ThingID = T.ThingID ORDER BY PriceDateTime DESC) Price 
FROM Thing T 
WHERE ThingID IN (1,2,3,4) AND DATEDIFF(D, PriceDateTime, GETDATE()) = 0 
GROUP BY ThingID 
-1

शायद मैं taks missunderstood लेकिन क्या एक के बारे में:

SELECT ID, ThingID, max(PriceDateTime), Price FROM Thing GROUP BY ThingID

0

यह बुद्धि से काम करना चाहिए एक वैश्विक पीके कॉलम (उदाहरण के लिए जटिल प्राथमिक कुंजी के लिए) का उपयोग करें:

SELECT t1.*, t2.PriceDateTime AS bigger FROM Prices t1 
LEFT JOIN Prices t2 ON t1.ThingID = t2.ThingID AND t1.PriceDateTime < t2.PriceDateTime 
HAVING t2.PriceDateTime IS NULL 
संबंधित मुद्दे