2012-04-19 12 views
5

विलय करने के लिए चुने गए 2 या अधिक पंक्तियों को देखते हुए, उनमें से एक को टेम्पलेट पंक्ति के रूप में पहचाना जाता है। अन्य पंक्तियों को अपने डेटा को टेम्पलेट के किसी भी शून्य मान कॉलम में विलय करना चाहिए।कुछ शर्तों के साथ पंक्तियों को एक पंक्ति में विलय करने का प्रयास

उदाहरण डेटा:

Id Name  Address   City   State Active Email    Date 
1 Acme1 NULL    NULL   NULL NULL [email protected]  3/1/2011 
2 Acme1 1234 Abc Rd  Springfield OR  0  [email protected] 1/12/2012 
3 Acme2 NULL    NULL   NULL 1  [email protected] 4/19/2012 

कहो कि एक उपयोगकर्ता टेम्पलेट पंक्ति के रूप में पहचान 1 के साथ पंक्ति को चुन लिया है, और आईडी 2 और 3 के साथ पंक्तियों पंक्ति 1 में मर्ज करने के लिए और फिर उन्हें हटा रहे हैं। पंक्ति आईडी 1 में किसी भी शून्य मान कॉलम को भरना चाहिए (यदि कोई मौजूद है) सबसे हालिया (दिनांक कॉलम देखें) गैर-शून्य मान, और पंक्ति आईडी 1 में पहले से मौजूद गैर-शून्य मानों को छोड़ दिया जाना चाहिए। उपरोक्त डेटा पर इस क्वेरी के परिणाम वास्तव में यह होना चाहिए:

Id Name  Address   City   State Active Email    Date 
1 Acme1 1234 Abc Road Springfield OR  1  [email protected]  3/1/2011 

सूचना सक्रिय मान 1 है, और नहीं 0 क्योंकि पंक्ति क्रमांक 3 सबसे हाल ही में तारीख थी।

पीएस साथ ही, क्या यह स्पष्ट रूप से परिभाषित/जानने के बिना ऐसा करने का कोई तरीका है कि सभी कॉलम नाम क्या हैं? जिन वास्तविक तालिका के साथ मैं काम कर रहा हूं उनमें कॉलम का एक टन है, जिसमें हर समय नए जोड़े जा रहे हैं। क्या तालिका में सभी कॉलम नाम देखने का कोई तरीका है, और उसके बाद उस सबक्वायरी या नौकरी करने के लिए मोहक उपयोग करें?

+0

क्या ईमेल भी [email protected] होना चाहिए, या Acme2 विलय के लिए सीमाओं से बाहर माना जाता है? –

+0

आप डेटा के समूह की पहचान कैसे करते हैं? मुझे लगता है कि आप डेटा के दोहरे विलय करना चाहते हैं। तो आप अपनी तालिका में हजारों या अधिक रिकॉर्ड में डेटा के समूह की पहचान कैसे करते हैं? यदि आप जानते हैं कि आप इसे संग्रहीत प्रक्रिया को लिख सकते हैं। यदि आवश्यक हो तो मैं उत्तर के रूप में नमूना एसपी लिख सकता हूं – YvesR

+0

हाय रसेल फॉक्स, नहीं, ईमेल [email protected] रहना चाहिए क्योंकि यह पहले से ही पंक्ति आईडी 1 में मौजूद था। आईडी 1 में केवल शून्य मूल्य कॉलम को संशोधित किया जाना चाहिए। – noahC

उत्तर

2

आप टेम्पलेट ध्वज द्वारा पहले पंक्तियों को क्रमबद्ध करके, फिर तिथि से नीचे कर सकते हैं। टेम्पलेट पंक्ति हमेशा अंतिम होना चाहिए। प्रत्येक पंक्ति को उस क्रम में एक संख्या असाइन की जाती है। अधिकतम() का उपयोग करके हम मुट्ठी पर कब्जा कर रहे सेल (संख्याओं के अवरोही क्रम में) ढूंढ रहे हैं। फिर हम उन अधिकतम मिलान से मेल खाने वाली पंक्तियों के कॉलम का चयन करते हैं।

; with rows as (
    select test.*, 
    -- Template row must be last - how do you decide which one is template row? 
    -- In this case template row is the one with id = 1 
    row_number() over (order by case when id = 1 then 1 else 0 end, 
         date) rn 
    from test 
    -- Your list of rows to merge goes here 
    -- where id in (...) 
), 
-- Finding first occupied row per column 
positions as (
    select 
    max (case when Name is not null then rn else 0 end) NamePosition, 
    max (case when Address is not null then rn else 0 end) AddressPosition, 
    max (case when City is not null then rn else 0 end) CityPosition, 
    max (case when State is not null then rn else 0 end) StatePosition, 
    max (case when Active is not null then rn else 0 end) ActivePosition, 
    max (case when Email is not null then rn else 0 end) EmailPosition, 
    max (case when Date is not null then rn else 0 end) DatePosition 
    from rows 
) 
-- Finally join this columns in one row 
select 
    (select Name from rows cross join Positions where rn = NamePosition) name, 
    (select Address from rows cross join Positions where rn = AddressPosition) Address, 
    (select City from rows cross join Positions where rn = CityPosition) City, 
    (select State from rows cross join Positions where rn = StatePosition) State, 
    (select Active from rows cross join Positions where rn = ActivePosition) Active, 
    (select Email from rows cross join Positions where rn = EmailPosition) Email, 
    (select Date from rows cross join Positions where rn = DatePosition) Date 
from test 
-- Any id will suffice, or even DISTINCT 
where id = 1 

You might check it at Sql Fiddle

संपादित करें:

क्रॉस अंतिम अनुभाग में जुड़ जाता है वास्तव में आंतरिक हो सकता है पर मिलती है rows.rn = xxxPosition। यह इस तरह से काम करता है, लेकिन आंतरिक जुड़ाव में परिवर्तन एक सुधार होगा।

1

यह इतना जटिल नहीं है।

पहले तो .. DECLARE @templateID INT = 1 आप याद कर सकते ..so जो पंक्ति टेम्पलेट के रूप में व्यवहार किया जाता है ..

अब नवीनतम NOT NULL मूल्यों (टेम्पलेट पंक्ति को बाहर) पाते हैं। सबसे आसान तरीका है प्रत्येक स्तंभ के लिए TOP 1 सबक्वेरी उपयोग करने के लिए है:

SELECT 
(SELECT TOP 1 Name FROM DataTab WHERE Name IS NOT NULL AND NOT ID = @templateID ORDER BY Date DESC) AS LatestName, 
(SELECT TOP 1 Address FROM DataTab WHERE Address IS NOT NULL AND NOT ID = @templateID ORDER BY Date DESC) AS AddressName 
-- add more columns here 

CTE में ऊपर लपेटें (आम तालिका अभिव्यक्ति) ताकि आप अपने UDPATE के लिए अच्छा इनपुट है ..

WITH Latest_CTE (CTE_LatestName, CTE_AddressName) -- add more columns here; I like CTE prefix to distinguish source columns from target columns.. 
AS 
-- Define the CTE query. 
(
    SELECT 
    (SELECT TOP 1 Name FROM DataTab WHERE Name IS NOT NULL AND NOT ID = @templateID ORDER BY Date DESC) AS LatestName, 
    (SELECT TOP 1 Address FROM DataTab WHERE Address IS NOT NULL AND NOT ID = @templateID ORDER BY Date DESC) AS AddressName 
    -- add more columns here 
) 
UPDATE 
<update statement here (below)> 

अब, स्मार्ट करना UPDATE अपने टेम्पलेट पंक्ति ISNULL का उपयोग करने का - यह रूप में सशर्त अद्यतन कार्य करेगा - अद्यतन केवल तभी लक्ष्य स्तंभ रिक्त है

WITH 
<common expression statement here (above)> 
UPDATE DataTab 
SET 
Name = ISNULL(Name, CTE_LatestName), -- if Name is null then set Name to CTE_LatestName else keep Name as Name 
Address = ISNULL(Address, CTE_LatestAddress) 
-- add more columns here.. 
WHERE ID = @templateID 

और अंतिम कार्य पंक्तियों को अन्य टेम्पलेट पंक्ति हटा देता है ..

DELETE FROM DataTab WHERE NOT ID = @templateID 

साफ़ करें?

+0

मुझे लगता है कि यह समझ में आता है, और मैं इसे जाने दूंगा, लेकिन बिना किसी कॉलम नाम के स्पष्ट रूप से परिभाषित/जाने बिना इसे करने के लिए कोई रास्ता संभव है? जिन वास्तविक तालिका के साथ मैं काम कर रहा हूं उनमें कॉलम का एक टन है, जिसमें हर समय नए जोड़े जा रहे हैं। क्या तालिका में सभी कॉलम नाम देखने का कोई तरीका है, और उसके बाद उस सबक्वायरी या नौकरी करने के लिए मोहक उपयोग करें? – noahC

+0

अपनी तालिका से सभी कॉलम नाम प्राप्त करने के लिए sys.columns कैटलॉग दृश्य का उपयोग करें (इस सेट से आईडी कॉलम बहिष्कृत करें)। अब 1) गतिशील एसक्यूएल का उपयोग करें - मुझे लगता है कि सिरदर्द मुझे लगता है और प्रदर्शन नीचे चला जाता है। या 2) sys.columns से टेक्स्ट फ़ाइल में आउटपुट कॉलम नाम, फिर "टेम्पलेट" का उपयोग करके लक्ष्य स्क्रिप्ट उत्पन्न करने के लिए AWK/GAWK प्रोग्राम का उपयोग करें। भविष्य में उपयोग के लिए अपना GAWK- प्रोग्राम सहेजें (अपनी SQL स्क्रिप्ट को पुनर्निर्माण/रीफ्रेश करें)। – huhu78

+0

विचार प्राप्त करने के लिए मेरे GAWK- समाधानों को किसी अन्य SQL-problem पर देखें: http://stackoverflow.com/a/10122169/1280816 मुझे यकीन है कि आपके पास बहुत सारे कॉलम हैं लेकिन अंतिम उत्पाद में कॉलम की संख्या स्थिर होगी । कॉलम नामों के माध्यम से प्रत्येक क्वेरी निष्पादन और लूप पर प्रदर्शन को खोने के बजाय बाह्य SQL स्क्रिप्ट जनरेटर करना बेहतर है। – huhu78

1

गतिशील कॉलम के लिए, आपको गतिशील एसक्यूएल का उपयोग करके समाधान लिखना होगा।

आप कॉलम की सूची प्राप्त करने के लिए sys.columns और sys.tables से पूछ सकते हैं, फिर आप उस कॉलम के लिए पहली गैर-शून्य पंक्ति ढूंढने और अपनी आउटपुट पंक्ति को अपडेट करने के लिए प्रत्येक नल कॉलम के लिए एक बार पीछे लूप करना चाहते हैं वह कॉलम एक बार जब आप लूप में 0 प्राप्त कर लेंगे तो आपके पास एक पूर्ण पंक्ति होगी जिसे आप उपयोगकर्ता को प्रदर्शित कर सकते हैं।

+0

आपके उत्तर के लिए धन्यवाद; ऐसा लगता है कि मुझे क्या करना है, लेकिन मुझे कुछ कोड देखने के बिना ठोस ठोसता होने में मुश्किल हो रही है। यदि यह बहुत परेशानी नहीं है, तो क्या आप यहां कुछ कोड फेंक सकते हैं ताकि मैं बेहतर समझ सकूं? बहुत बहुत धन्यवाद। – noahC

1

मुझे पोस्टिंग तिथियों पर ध्यान देना चाहिए। किसी भी मामले में, अद्यतन विवरण बनाने के लिए गतिशील एसक्यूएल का उपयोग करके यहां एक समाधान है। इसे किसी भी तरह से बनाने के लिए आपको कुछ देना चाहिए।

रास्ते में परिणामों को प्रमाणित करने के लिए वहां कुछ अतिरिक्त कोड है, लेकिन मैंने इस तरह से टिप्पणी करने की कोशिश की जिसने गैर-महत्वपूर्ण कोड स्पष्ट किया।

CREATE TABLE 
dbo.Dummy 
    (
    [ID] int , 
    [Name] varchar(30), 
    [Address] varchar(40) null, 
    [City] varchar(30) NULL, 
    [State] varchar(2) NULL, 
    [Active] tinyint NULL, 
    [Email] varchar(30) NULL, 
    [Date] date NULL 
    ); 
-- 
INSERT dbo.Dummy 
VALUES 
(
    1, 'Acme1', NULL, NULL, NULL, NULL, '[email protected]', '3/1/2011' 
) 
, 
(
    2, 'Acme1', '1234 Abc Rd', 'Springfield', 'OR', 0, '[email protected]', '1/12/2012' 
) 
, 
(
    3, 'Acme2', NULL, NULL, NULL, 1, '[email protected]', '4/19/2012' 
); 
DECLARE 
    @TableName nvarchar(128) = 'Dummy', 
    @TemplateID int = 1, 
    @SetStmtList nvarchar(max) = '', 
    @LoopCounter int = 0, 
    @ColumnCount int = 0, 
    @SQL nvarchar(max) = '' 
    ; 
-- 
--Create a table to hold the column names 
DECLARE  
    @ColumnList table 
     (
     ColumnID tinyint IDENTITY, 
     ColumnName nvarchar(128) 
     ); 
-- 
--Get the column names 
INSERT @ColumnList 
(
    ColumnName 
) 
    SELECT 
     c.name 
    FROM 
     sys.columns AS c 
     JOIN 
     sys.tables AS t 
      ON 
       t.object_id = c.object_id 
    WHERE 
     t.name = @TableName; 
-- 
--Create loop boundaries to build out the SQL statement 
SELECT 
    @ColumnCount = MAX(l.ColumnID), 
    @LoopCounter = MIN (l.ColumnID) 
FROM 
    @ColumnList AS l; 
-- 
--Loop over the column names 
WHILE @LoopCounter <= @ColumnCount 
BEGIN 
    --Dynamically construct SET statements for each column except ID (See the WHERE clause) 
    SELECT 
     @SetStmtList = @SetStmtList + ',' + l.ColumnName + ' =COALESCE(' + l.ColumnName + ', (SELECT TOP 1 ' + l.ColumnName + ' FROM ' + @TableName + ' WHERE ' + l.ColumnName + ' IS NOT NULL AND ID <> ' + CAST(@TemplateID AS NVARCHAR(MAX)) + ' ORDER BY Date DESC)) ' 
    FROM 
     @ColumnList AS l 
    WHERE 
     l.ColumnID = @LoopCounter 
     AND 
     l.ColumnName <> 'ID'; 
-- 
    SELECT 
     @LoopCounter = @LoopCounter + 1; 
-- 
END; 

--TESTING - Validate the initial table values 
SELECT * FROM dbo.Dummy ; 
-- 
--Get rid of the leading common in the SetStmtList 
SET @SetStmtList = SUBSTRING(@SetStmtList, 2, LEN(@SetStmtList) - 1); 
--Build out the rest of the UPDATE statement 
SET @SQL = 'UPDATE ' + @TableName + ' SET ' + @SetStmtList + ' WHERE ID = ' + CAST(@TemplateID AS NVARCHAR(MAX)) 
--Then execute the update 
EXEC sys.sp_executesql 
    @SQL; 
-- 
--TESTING - Validate the updated table values 
SELECT * FROM dbo.Dummy ; 
-- 
--Build out the DELETE statement 
SET @SQL = 'DELETE FROM ' + @TableName + ' WHERE ID <> ' + CAST(@TemplateID AS NVARCHAR(MAX)) 
--Execute the DELETE 
EXEC sys.sp_executesql 
    @SQL; 
-- 
--TESTING - Validate the final table values 
SELECT * FROM dbo.Dummy; 
-- 
DROP TABLE dbo.Dummy; 
संबंधित मुद्दे