2016-05-25 18 views
5

Ho una tabella contenente un elenco di parole e wordform associati. I dati tipici nella tabella sono riportati di seguito. Nota alcune delle colonne di WordForms terminano, ad es. e alcuni solo terminare con l'ultima parola wordformCome posso popolare una tabella padre e una tabella figlio da un'altra tabella?

Id Word  WordForms 
1 abandon abandoned, abandoning, abandonment, abandons  
2 abstract abstraction, abstractions, abstractly, abstracts, e.g. 

Ecco il layout della tabella di dati di origine:

CREATE TABLE [dbo].[TempWords] 
(
    [Id]   INT   IDENTITY (1, 1) NOT NULL, 
    [Word]  NVARCHAR (MAX) NOT NULL, 
    [WordForms] NVARCHAR (MAX) NULL, 
) 

Vorrei utilizzare questi dati per compilare due tabelle. So di usare SQL INSERT INTO ma penserò che mi aiuti solo con l'unica tabella. Quello che mi piacerebbe fare è prendere la prima parola, metterla nella tabella Words e quindi separare le wordform che ora sono divise da una virgola e metterle nella tabella di WordForms.

CREATE TABLE [dbo].[Words] 
(
    [WordId]  INT   IDENTITY (1, 1) NOT NULL, 
    [Word]  NVARCHAR (MAX) NOT NULL 
) 

CREATE TABLE [dbo].[WordForms] 
(
    [Id]   INT   IDENTITY (1, 1) NOT NULL, 
    [WordId]  INT NOT NULL, 
    [Text] NVARCHAR (MAX) NULL, 
) 

Qualcuno può darmi qualche consiglio su come posso farlo?

+0

si potrebbe mostrare esempio di come si vuole separare i dati tempwords (con i dati mostrato in questione) ad altri tavoli – TheGameiswar

risposta

7

Prima di tutto, è possibile creare la funzione UDF per separare i valori CSV.

CREATE FUNCTION dbo.fn_Split (
     @InputString     VARCHAR(8000), 
     @Delimiter     VARCHAR(50) 
) 

RETURNS @Items TABLE (
     Item       VARCHAR(8000) 
) 

AS 
BEGIN 
     IF @Delimiter = ' ' 
     BEGIN 
      SET @Delimiter = ',' 
      SET @InputString = REPLACE(@InputString, ' ', @Delimiter) 
     END 

     IF (@Delimiter IS NULL OR @Delimiter = '') 
      SET @Delimiter = ',' 

--INSERT INTO @Items VALUES (@Delimiter) -- Diagnostic 
--INSERT INTO @Items VALUES (@InputString) -- Diagnostic 

     DECLARE @Item     VARCHAR(8000) 
     DECLARE @ItemList  VARCHAR(8000) 
     DECLARE @DelimIndex  INT 

     SET @ItemList = @InputString 
     SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0) 
     WHILE (@DelimIndex != 0) 
     BEGIN 
      SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex) 
      INSERT INTO @Items VALUES (@Item) 

      -- Set @ItemList = @ItemList minus one less item 
      SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)[email protected]) 
      SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0) 
     END -- End WHILE 

     IF @Item IS NOT NULL -- At least one delimiter was encountered in @InputString 
     BEGIN 
      SET @Item = @ItemList 
      INSERT INTO @Items VALUES (@Item) 
     END 

     -- No delimiters were encountered in @InputString, so just return @InputString 
     ELSE INSERT INTO @Items VALUES (@InputString) 

     RETURN 

END -- End Function 
GO 

Quindi è possibile utilizzare le istruzioni INSERT sotto per popolare le tabelle.

INSERT INTO [Words] 
SELECT Word FROM TempWords 


INSERT INTO WordForms 

SELECT 
    W.WordId, 
    LTRIM(RTRIM(FNT.Item)) AS Item 
FROM TempWords AS TW 
INNER JOIN [Words] AS W 
ON TW.[Word]=W.[Word] 
CROSS APPLY fn_Split(REPLACE(TW.WordForms,', e.g.',''),',') AS FNT 


SELECT * FROM [Words] 
SELECT * FROM WordForms 
+0

Grazie mille per i vostri ans molto dettagliati wer. Lo metterò alla prova e ti aggiornerò. Una cosa forse manca. La colonna delle wordform nei dati originali a volte ha ", ad es." alla fine dell'elenco di parole e a volte no. C'è un modo in cui questo potrebbe essere filtrato? In caso contrario, penso che popolerà la tabella di WordForms con molte righe con testo ", ad es." - Grazie –

+1

Ho aggiornato il codice per includere la funzione REPLACE dalla colonna 'WordForm'. Rimuoverà tutte le occorrenze di ", ad es." dalla colonna 'WordForm'. – Sandesh

+0

@SandeshP - Dovrò testare la soluzione su alcuni dati di test prima delle tabelle reali. Aggiornerò la tua risposta e ti farò sapere come sono andate le cose, ma ci vorrà un po 'prima che controlli tutto. Grazie per l'aiuto. Spero di essere in grado di accettare presto la domanda. Si prega di controllare se ho più commenti in seguito. –

5

È possibile inserire le parole in prima tabella, di forme di parola analizzare e inserirle nella tabella figlio con il collegamento a tabella padre.

Link tabella padre può essere ottenuto unendo il word colonna (credo che sia univoco) o facendo qualche cosa MERGE + OUTPUT di ottenere in un solo passaggio SOURCE.ID (da @words_csv) e INSERTED.ID. In qualsiasi modo ti piaccia

L'analisi può essere implementata in molti modi, controllare questo esempio (in realtà non lo consiglierei a analizzare con sql affatto).

DECLARE @words_csv TABLE (Id INT IDENTITY(1, 1), Word VARCHAR(100), WordForms VARCHAR(1000)) 

INSERT INTO @words_csv(word, wordforms) 
VALUES 
    ('abandon', 'abandoned, abandoning, abandonment, abandons, e.g.'), 
    ('abstract', 'abstraction, abstractions, abstractly, abstracts') 

--INSERT INTO [dbo].[Words](word) 
--SELECT w.word 
--FROM @words_csv w 

;WITH word_forms_extracted AS 
    (
     SELECT w.id, 
       w.word, 
       ltrim(rtrim(cast(case when CHARINDEX(',', w.WordForms) > 0 then substring(w.wordforms, 1, CHARINDEX(',', w.WordForms)-1) end AS VARCHAR(1000)))) wordform, 
       stuff(w.wordforms, 1, CHARINDEX(',', w.WordForms), '') wordforms 
     FROM @words_csv w 

     UNION ALL 

     SELECT w.id, 
       w.word, 
       ltrim(rtrim(cast(case when CHARINDEX(',', wfe.WordForms) > 0 then substring(wfe.wordforms, 1, CHARINDEX(',', wfe.WordForms)-1) else wfe.wordforms end AS VARCHAR(1000)))) wordform, 
       case when CHARINDEX(',', wfe.WordForms) > 0 then stuff(wfe.wordforms, 1, CHARINDEX(',', wfe.WordForms), '') ELSE '' end wordforms 
     FROM @words_csv w 
       INNER JOIN word_forms_extracted wfe 
        ON wfe.id = w.id 
     WHERE wfe.wordforms != '' 
    ) 
SELECT wf.id, wf.word, wf.wordform 
FROM word_forms_extracted wf 
--INNER JOIN [dbo].[Words] w 
--ON w.word = wf.word 
WHERE wf.wordform NOT IN ('', 'e.g.') 
ORDER BY wf.id, wf.wordform 
OPTION(MAXRECURSION 1000) 

finale SELECT può essere facilmente modificato per INSERT INTO dbo.WordForms (...) SELECT ... Link dbo.Words si ottiene qui come si può vedere unendo il word colonna.

+0

Ciao Ivan, alcune delle righe di dati sorgente ", ad esempio" alla fine di loro e altri no. Potresti modificare il codice per rifiutare ", ad es." dove appare alla fine dei dati di origine nella colonna di WordForms? –

+0

Poiché abbiamo ottenuto forme di parola analizzate, ad esempio " può essere filtrato con il semplice predicato 'WHERE'. O come ha fatto @Sandesh - rimuovendo prima l'analisi. E commentato le tue tabelle statiche per rendere questo esempio facile da eseguire e testare. –

+0

In realtà questo script sta perdendo l'ultima forma, verrà risolto un po 'più tardi. Fatto. –

3

Con l'aiuto di XML:

INSERT INTO [dbo].[Words] 
SELECT DISTINCT [Word] 
FROM [dbo].[TempWords] 

DECLARE @xml xml 

SELECT @xml = (
    SELECT CAST('<row><word>'+WORD+'</word><w>' + REPLACE(WordForms,', ','</w><w>') +'</w></row>' as xml) 
    FROM [dbo].[TempWords] 
    FOR XML PATH('') 
    ) 


INSERT INTO [dbo].[WordForms] 
SELECT w.[WordId], 
     t.v.value('.','nvarchar(max)') as [Text] 
FROM @xml.nodes('/row/w') as t(v) 
LEFT JOIN [dbo].[Words] w 
    ON t.v.value('../word[1]','nvarchar(max)') = w.[Word] 

In [dbo].[Words]

WordId Word 
1  abandon 
2  abstract 

In [dbo].[WordForms]

Id WordId Text 
1 1  abandoned 
2 1  abandoning 
3 1  abandonment 
4 1  abandons 
5 2  abstraction 
6 2  abstractions 
7 2  abstractly 
8 2  abstracts 
Problemi correlati