[T-SQL] string uit een xml lezen die in de db opgeslagen is

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Motrax
  • Registratie: Februari 2004
  • Niet online

Motrax

Profileert

Topicstarter
Heren,

Meestal weet ik wel ongeveer hoe ik het e.e.a. moet aanpakken als het goed SQL queries schrijven. Als ik het niet weet, dan is er nog een leuk stukje google.

Maar... ik heb nu een geval waar ik niet eens van weet hoe ik het moet aanpakken en heb hier dus hulp bij nodig.

Ik heb uiteraard gezocht, met steek worden als 'SQL read string with unknown length', 'SQL read xml' etc.

Het probleem:
Ik probeer een aantal instellingen van een applicatie uit te lezen uit de database. Hiervan moet ik een aantal specifieke instellingen hebben. De applicatie zelf leest waarschijnlijk deze tabel ook uit, maar is een gesloten boek.

De data is opgeslagen in LANGXMLTEXT met de volgende attributen:
OBJECT_KEY
SEQNUM
TEXT_VALUE

In deze attributen is XML opgeslagen in de volgende structuur:
code:
1
2
3
4
5
OBJECT_KEY  SEQ_NUM      TEXT_VALUE
     1         1         <OBJECT="OBJECTNAAM ID="GUID"><OPTIONS>VEELOPTIE
     1         2         SNOGMEEROPTIESETC
     1         3         filename="Excelsheet.xls"</OPTIONS>
     1         4         </OBJECT>
Gegeven voorbeeld opent en sluit de attributen misschien niet goed, maar het is een voorbeeld.

Ik moet nu via SQL de file name ophalen. Wat is het probleem?
- Waar het om gaat is dat er een onbekende hoeveelheid velden zijn, dus de locatie adh van de seq_num zegt niets, er is dus een onbekende diepte.
- Regels kunnen bruut afgekapt zijn en vervolgen op een volgend seq_num (op te lossen door alles te concatineren, maar tot welke diepte?)
- De filename attribuut is uniek in de xml structuur
- De filename is van onbekende lengte, maar heeft wel een minimum en maximum en wordt geopend en gesloten met "

Heeft iemand enig idee hoe ik dit zou moeten aanpakken? Ik weet niet eens waar ik moet beginnen...

Op dit moment wordt er met MQ SQL gewerkt, maar kan in de toekomst ook Oracle worden. De queries moeten zoveel mogelijk database onafhankelijk worden, maar ik snap dat in dit geval het wat lastiger kan worden.

☻/
/▌
/ \ Analyseert | Modelleert | Valideert | Solliciteert | Generaliseert | Procrastineert | Epibreert |


Acties:
  • 0 Henk 'm!

Verwijderd

Aangezien je niet weet hoe lang de data is kun je toch gewoon het datatype 'text' gebruiken, waarbij je geen lengte hoeft te definiëren, zodat je alsnog alles kunt concatten en vervolgens doorzoeken?
Misschien een erg naïeve oplossing, maar wel eenvoudig en werkbaar.

[ Voor 29% gewijzigd door Verwijderd op 07-12-2010 18:29 ]


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Motrax schreef op dinsdag 07 december 2010 @ 17:58:
Ik moet nu via SQL de file name ophalen. Wat is het probleem?
Ik trap de open deur toch even in: Dit is TDWTF.com waardig :X
Motrax schreef op dinsdag 07 december 2010 @ 17:58:
- Waar het om gaat is dat er een onbekende hoeveelheid velden zijn, dus de locatie adh van de seq_num zegt niets, er is dus een onbekende diepte.
Maar heb je binnen 1 object_key niet 1 "xml document" te pakken?
Dus is het niet zo:
code:
1
2
3
4
5
6
7
8
9
OBJECT_KEY  SEQ_NUM      TEXT_VALUE
     1         1         <OBJECT="OBJECTNAAM ID="GUID"><OPTIONS>VEELOPTIE
     1         2         SNOGMEEROPTIESETC
     1         3         filename="Excelsheet.xls"</OPTIONS>
     1         4         </OBJECT>
     2         1         <OBJECT="OBJECTNAAM2 ID="GUID2"><OPTIONS>VEELOPTIE
     2         2         S2NOGMEEROPTIESETC
     2         3         filename="Excelsheet2.xls"</OPTIONS>
     2         4         </OBJECT>

? In dat geval kun je een enkel object ophalen met iets als:
SQL:
1
2
3
4
select ...
from mytable
where object_key = 1
order by seq_num

En dan door de records heen wandelen en de string concatten. Wil je meerdere objecten ophalen dan pas je de where aan met een "where object_key in (1, 2, ...)" of als je alles wil hebben laat je de where achterwege. In elk geval rag je de resultaatset van de query door een parsedocuments functie:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function parsedocuments(records) {
  currentkey = null
  mydocument = ''
  foreach records as r {
    if currentkey != r.object_key {
      parsedocument(mydocument)
      currentkey = r.object_key
    }
    mydocument += r.text_value
  }
  parsedocument(mydocument)
}

function parsedocument(xmlstring) {
  if (xmlstring != null) {
    //Parse XML string here.
  }
}
Motrax schreef op dinsdag 07 december 2010 @ 17:58:
- Regels kunnen bruut afgekapt zijn en vervolgen op een volgend seq_num (op te lossen door alles te concatineren, maar tot welke diepte?)
Zie vorige opmerking; dat zou betekenen dat je bij elke nieuwe object_key die je tegen komt een nieuw document kunt parsen (en dus binnen 1 object_key de strings concatten).
Motrax schreef op dinsdag 07 december 2010 @ 17:58:
- De filename attribuut is uniek in de xml structuur
En in hoeverre heb je de garantie dat "filename=" niet/wel voorkomt in "VEELOPTIESNOGMEEROPTIESETC" of elders in de tekst? Want je maakt aannames die je (toch al kazige) oplossing (waar je dan zelf niet schuld aan bent weliswaar) nog een behoorlijk stuk fragieler maakt.
Motrax schreef op dinsdag 07 december 2010 @ 17:58:
- De filename is van onbekende lengte, maar heeft wel een minimum en maximum en wordt geopend en gesloten met "
Lijkt me weinig relevant en sowieso niet een factor waar je op wil vertrouwen.

Je ontkomt, IMHO, niet aan het fatsoenlijk parsen van het (bijeengesprokkelde en daarna met plakband bijeen geprakte) XML document. Een DB-onafhankelijke manier van deze meuk uitspitten in SQL statements alléén lijkt me haast onmogelijk (en anders gaat er een shitload aan tijd in zitten om 't goed/betrouwbaar te krijgen). Met MSSQL (of andere RDBMS) specifieke functies kom je nog een heel eind maar dan nog kom je de flarden XML als een 'heel document' aan 't benaderen wil je 't betrouwbaar hebben lijkt me.

[ Voor 44% gewijzigd door RobIII op 07-12-2010 19:07 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • The Eagle
  • Registratie: Januari 2002
  • Laatst online: 13:26

The Eagle

I wear my sunglasses at night

Leuke uitdaging :)
En geef alsjeblieft degene die dit onzalige idee heeft bedacht een trap onder zijn hol :P

Vooraleerst: de simpelste oplossing is denk ik het gebruik van een function based view. Ik weet alleen niet of je zomaar een typecasting zou kunnen doen van een hele zut aan geconcateneerde string values naar een long, maar dat kun je uitproberen :) En dan met subs en instr op die long zaken ophalen (gaat ook niet zomaar, maar daar zijn wel trucjes voor te vinden :) Desnoods icm een stored procedure die gaat zoeken in een long op een string en die weer in een view hangen oid ;)

Wordt het begin van de filename aangeduid met een " of met filename=" ? Dat zou je sowieso al eens kunnen helpen, omdat je dan begin en einde uniek kunt identificeren binnen een gecombineerde sleutelwaarde van OBJECT_KEY en SEQ_NUM.

Verder moet het begin van de filenaam zo te zien OF in dezelfde seqnum, OF in een andere seqnum van dezelfde object key zitten. Dat helpt ook met het maken van je where-clause. Dat wordt een dubbele EXISTS zo te zien ;) Dat je de waarde van de op te halen object_key's en seq_num's moest bepalen met die instr en substr had je vast zelf ook al gevonden ;)

Btw, welk pakket is dit? Neigt naar PeopleSoft of iig een Oracle smaak namelijk :9

Al is het nieuws nog zo slecht, het wordt leuker als je het op zijn Brabants zegt :)


Acties:
  • 0 Henk 'm!

  • Skinny
  • Registratie: Januari 2000
  • Laatst online: 19-09 16:45

Skinny

DIRECT!

Ik weet niet hoe groot de tabel is en hoe vaak dit soort queries moeten worden uitgevoerd, maar een quick en dirty oplossing zou de volgende functie kunnen zijn :

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE FUNCTION dbo.GetObjectTextValue
(
    @ObjectKey int
) RETURNS nvarchar(500)
AS
BEGIN

DECLARE @ObjectTextValue nvarchar(500)
SET @ObjectTextValue = '' -- set initial value to avoid concatting NULL

SELECT @ObjectTextValue = @ObjectTextValue + TEXT_VALUE
FROM SourceTable t
WHERE OBJECT_KEY = @ObjectKey
ORDER BY SEQ_NUM

DECLARE @Position int
SET @Position =PATINDEX('%filename="%', @ObjectTextValue) -- get filename start

SET @ObjectTextValue = SUBSTRING( @ObjectTextValue, @Position+10, LEN(@ObjectTextValue)-@Position+11) -- strip left side

SET @Position =PATINDEX('%"%', @ObjectTextValue) -- get end quote

SET @ObjectTextValue = SUBSTRING( @ObjectTextValue, 1,@Position - 1) -- strip right side

RETURN @ObjectTextValue
END


Om op te halen :
SQL:
1
2
3
-- Retrieve Results 
SELECT DISTINCT OBJECT_KEY, dbo.GetObjectTextValue(OBJECT_KEY) AS FileName
FROM SourceTable


levert :

code:
1
2
3
OBJECT_KEY   FileName
1                      Excelsheet1.xls
2                      Excelsheet2.xls


Hierbij ben ik ervanuit gegaan dat die filename="Excelsheet1.xls" uniek is binnen de hele dataset per OBJECT_KEY. Is dit niet het geval zal je de functie wat complexer moeten maken.

Nogmaals, dit is quick en dirty en niet bedoeld om op grote tabellen heel frequent uit te voeren zonder goed te weten wat de impact op je performance is.

SIZE does matter.
"You're go at throttle up!"


Acties:
  • 0 Henk 'm!

  • Motrax
  • Registratie: Februari 2004
  • Niet online

Motrax

Profileert

Topicstarter
Verwijderd schreef op dinsdag 07 december 2010 @ 18:28:
Aangezien je niet weet hoe lang de data is kun je toch gewoon het datatype 'text' gebruiken, waarbij je geen lengte hoeft te definiëren, zodat je alsnog alles kunt concatten en vervolgens doorzoeken?
Misschien een erg naïeve oplossing, maar wel eenvoudig en werkbaar.
Maar de diepte is ook onbekend ;) Dus de filename kan op seq_num = 2 staan, maar ook op seq_num = 23.

Daarnaast kan ie ook nog eens afgebroken staan op twee seq_num's. Daarvoor is de oplossing om een hele lange string te maken, maar is voor mij de onbekende diepte een probleem.
RobIII schreef op dinsdag 07 december 2010 @ 18:30:
Ik trap de open deur toch even in: Dit is TDWTF.com waardig :X
Ja lach maar! :P

Het is voor documentatie doeleinden. Dus automatisch genereren van een hoop technische gegevens die handmatig te veel tijd gaan kosten. Dan kan er meer tijd worden gestoken in het functioneel documenteren van de ETL logica.

Het gaat hier overigens om het querien op een repository van een ETL tool. Normaal gesproken heeft niemand iets te zoeken in de repository en laat je de applicatie haar truukjes uitvoeren zoals ze dat zelf wel. Ik vermoed overigens dat de applicatie in een grijs verleden ooit niet op een db gebaseerd was. Maar goed, ik ben allang blij dat het human readable is.
Maar heb je binnen 1 object_key niet 1 "xml document" te pakken?
Dus is het niet zo:
code:
1
2
3
4
5
6
7
8
9
OBJECT_KEY  SEQ_NUM      TEXT_VALUE
     1         1         <OBJECT="OBJECTNAAM ID="GUID"><OPTIONS>VEELOPTIE
     1         2         SNOGMEEROPTIESETC
     1         3         filename="Excelsheet.xls"</OPTIONS>
     1         4         </OBJECT>
     2         1         <OBJECT="OBJECTNAAM2 ID="GUID2"><OPTIONS>VEELOPTIE
     2         2         S2NOGMEEROPTIESETC
     2         3         filename="Excelsheet2.xls"</OPTIONS>
     2         4         </OBJECT>

?
Correct. Sorry dat ik deze informatie niet heb gegeven.

De combinatie OBJECT_KEY en SEQ_NUM maakt het uniek. Één OBJECT_KEY is één xml-object.
In dat geval kun je een enkel object ophalen met iets als:
SQL:
1
2
3
4
select ...
from mytable
where object_key = 1
order by seq_num

En dan door de records heen wandelen en de string concatten. Wil je meerdere objecten ophalen dan pas je de where aan met een "where object_key in (1, 2, ...)" of als je alles wil hebben laat je de where achterwege.
Er zijn wel meerdere objecten waar ik de gegevens van nodig heb, maar daar heb ik een aparte query voor die de resultaatset limiteert tot de objecten die specifiek nodig zijn. Dus inderdaad een IN() statement.
In elk geval rag je de resultaatset van de query door een parsedocuments functie:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function parsedocuments(records) {
  currentkey = null
  mydocument = ''
  foreach records as r {
    if currentkey != r.object_key {
      parsedocument(mydocument)
      currentkey = r.object_key
    }
    mydocument += r.text_value
  }
  parsedocument(mydocument)
}

function parsedocument(xmlstring) {
  if (xmlstring != null) {
    //Parse XML string here.
  }
}
Ik ben (nog) niet thuis in functies... maar ik ontkom er niet aan vanwege de onbekende hoeveelheid SEQ_NUM's...
Zie vorige opmerking; dat zou betekenen dat je bij elke nieuwe object_key die je tegen komt een nieuw document kunt parsen (en dus binnen 1 object_key de strings concatten).
Correct.
En in hoeverre heb je de garantie dat "filename=" niet/wel voorkomt in "VEELOPTIESNOGMEEROPTIESETC" of elders in de tekst? Want je maakt aannames die je (toch al kazige) oplossing (waar je dan zelf niet schuld aan bent weliswaar) nog een behoorlijk stuk fragieler maakt.
Daar ben ik me bewust van. Ik moet dit controleren, maar een aantal spot checks laten zien dat er maar één filename is. Als er meerdere zijn, zijn deze wel allemaal relevant. Maar ik dacht dat er in dit object maar één was toegestaan, maar zal ik nog controleren.
Lijkt me weinig relevant en sowieso niet een factor waar je op wil vertrouwen.

Je ontkomt, IMHO, niet aan het fatsoenlijk parsen van het (bijeengesprokkelde en daarna met plakband bijeen geprakte) XML document. Een DB-onafhankelijke manier van deze meuk uitspitten in SQL statements alléén lijkt me haast onmogelijk (en anders gaat er een shitload aan tijd in zitten om 't goed/betrouwbaar te krijgen). Met MSSQL (of andere RDBMS) specifieke functies kom je nog een heel eind maar dan nog kom je de flarden XML als een 'heel document' aan 't benaderen wil je 't betrouwbaar hebben lijkt me.
In die zin is er wel op te vertrouwen dat als dit niet klopt, de ETL code invalide is en niet meer draait. Dan is het automatisch documenteren het kleinste probleem...
The Eagle schreef op dinsdag 07 december 2010 @ 18:39:
Leuke uitdaging :)
En geef alsjeblieft degene die dit onzalige idee heeft bedacht een trap onder zijn hol :P
Geef jij SAP even een trap dan :+
Vooraleerst: de simpelste oplossing is denk ik het gebruik van een function based view. Ik weet alleen niet of je zomaar een typecasting zou kunnen doen van een hele zut aan geconcateneerde string values naar een long, maar dat kun je uitproberen :) En dan met subs en instr op die long zaken ophalen (gaat ook niet zomaar, maar daar zijn wel trucjes voor te vinden :) Desnoods icm een stored procedure die gaat zoeken in een long op een string en die weer in een view hangen oid ;)

Wordt het begin van de filename aangeduid met een " of met filename=" ? Dat zou je sowieso al eens kunnen helpen, omdat je dan begin en einde uniek kunt identificeren binnen een gecombineerde sleutelwaarde van OBJECT_KEY en SEQ_NUM.
Met:
filename="
Verder moet het begin van de filenaam zo te zien OF in dezelfde seqnum, OF in een andere seqnum van dezelfde object key zitten. Dat helpt ook met het maken van je where-clause. Dat wordt een dubbele EXISTS zo te zien ;) Dat je de waarde van de op te halen object_key's en seq_num's moest bepalen met die instr en substr had je vast zelf ook al gevonden ;)
Dat weet ik nog net wel ;) Filename kan ook als volgt zijn afgekapt, het wordt echt bruut gedaan:
code:
1
2
3
SEQ_NUM  TEXT_VALUE
    22          optiesoptiesohisteinderegelmaareerstnogbeginvande...fil
    23          ename="
Btw, welk pakket is dit? Neigt naar PeopleSoft of iig een Oracle smaak namelijk :9
SAP Business Objects Data Services (BODS) om te precies te zijn, voormalig Business Objects Data Integrator (BODI). OWB genereert redelijke documentatie, maar BODS/BODI genereert wel wat, maar nooit echt precies wat je zelf wil.

Overigens is er een view in de repository welke datalineage weergeeft, dus van brontabel + bron attribuut naar target tabel + target attribuut, zelfs als het samengestelde attributen etc worden. Daar werd ik wel enthousiast van, want op basis hiervan kan je een datalineage generen. Maar goed, dat is een uitdaging voor later aangezien eerst de basis van de automatische documentatie moet staan.
Skinny schreef op dinsdag 07 december 2010 @ 23:18:
Ik weet niet hoe groot de tabel is en hoe vaak dit soort queries moeten worden uitgevoerd,
In principe zelden en ook nog eens zelden op productie. Na development kan de documentatie dan automatisch gegenereert worden per release.
maar een quick en dirty oplossing zou de volgende functie kunnen zijn :

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CREATE FUNCTION dbo.GetObjectTextValue
(
    @ObjectKey int
) RETURNS nvarchar(500)
AS
BEGIN

DECLARE @ObjectTextValue nvarchar(500)
SET @ObjectTextValue = '' -- set initial value to avoid concatting NULL

SELECT @ObjectTextValue = @ObjectTextValue + TEXT_VALUE
FROM SourceTable t
WHERE OBJECT_KEY = @ObjectKey
ORDER BY SEQ_NUM

DECLARE @Position int
SET @Position =PATINDEX('%filename="%', @ObjectTextValue) -- get filename start

SET @ObjectTextValue = SUBSTRING( @ObjectTextValue, @Position+10, LEN(@ObjectTextValue)-@Position+11) -- strip left side

SET @Position =PATINDEX('%"%', @ObjectTextValue) -- get end quote

SET @ObjectTextValue = SUBSTRING( @ObjectTextValue, 1,@Position - 1) -- strip right side

RETURN @ObjectTextValue
END


Om op te halen :
SQL:
1
2
3
-- Retrieve Results 
SELECT DISTINCT OBJECT_KEY, dbo.GetObjectTextValue(OBJECT_KEY) AS FileName
FROM SourceTable


levert :

code:
1
2
3
OBJECT_KEY   FileName
1                      Excelsheet1.xls
2                      Excelsheet2.xls


Hierbij ben ik ervanuit gegaan dat die filename="Excelsheet1.xls" uniek is binnen de hele dataset per OBJECT_KEY. Is dit niet het geval zal je de functie wat complexer moeten maken.
Dit wordt niet afgedwongen, maar als dit niet uniek is, dan is een file locking probleem omdat er tegelijkertijd naar hetzelfde bestand wordt weggeschreven. Dit kan dus in theorie voorkomen als er meerdere files per object zijn toegestaan (moet ik dus nog controleren), maar levert operationeel gezien problemen op.

In ieder geval dank voor het voorbeeld. Het is meer dan ik had verwacht (je moet tenslotte zélf zoeken ;) ), maar ik wist echt niet waar ik moest beginnen.
Nogmaals, dit is quick en dirty en niet bedoeld om op grote tabellen heel frequent uit te voeren zonder goed te weten wat de impact op je performance is.
Tabel is niet groot, heeft index op OBJECT_KEY en SEQ_NUM en ik limiteer met een andere query specifiek alleen op de objecten die ik nodig heb. Desnoods is er een kopie van de tabel te maken naar een andere db zodat er geen impact is op de performance. Gelukkig is performance impact op een operationeel systeem niet het probleem.


Allen, dank voor de antwoorden. Ik ga hier donderdag mee verder en kan ik of trots melden dat het gelukt is met eeuwig dank, of geef ik aan waar ik ben vastgelopen en wat ik heb uitgezocht :)

☻/
/▌
/ \ Analyseert | Modelleert | Valideert | Solliciteert | Generaliseert | Procrastineert | Epibreert |


Acties:
  • 0 Henk 'm!

Verwijderd

Motrax schreef op woensdag 08 december 2010 @ 00:03:
[...]
Maar de diepte is ook onbekend ;) Dus de filename kan op seq_num = 2 staan, maar ook op seq_num = 23.
Dat geeft toch niet? Je hebt toch je OBJECT_KEY? Kun je in T-SQL niet gewoon appenden aan text c.q. kun je in plaats van varchar, waarbij je een lengte moet opgeven, niet gewoon het datatype 'text' gebruiken, waarbij dat niet hoeft en je dus ook niet van te voren hoeft te weten hoe diep alles is.

[ Voor 24% gewijzigd door Verwijderd op 08-12-2010 14:35 ]


Acties:
  • 0 Henk 'm!

  • Motrax
  • Registratie: Februari 2004
  • Niet online

Motrax

Profileert

Topicstarter
Ik ben nu een eind verder. De functie van Skinny werkt perfect voor het zoeken in de samengestelde string.

Maar er gaat nog wel iets mis. Als ik het volgende blok hardcodeer op de juiste regel, dan komt de filenaam er perfect uit:
SQL:
1
2
3
4
5
SELECT @ObjectTextValue = @ObjectTextValue + TEXT_VALUE 
FROM LANGXMLTEXT t 
WHERE OBJECT_KEY = @ObjectKey 
AND SEQNUM = 23 -- Hardcoded regel!
ORDER BY SEQNUM ASC


Wat is er namelijk aan de hand? De selectie query haalt blijkbaar niet alle regels op, maar pakt alleen de laatste. Met ASC en DESC op de ORDER BY heb ik dit gecontroleerd. De inhoud van @ObjectTextValue is of de eerste SEQNUM (DESC) of de laatste SEQNUM (ASC). Het lijkt er op dat het samenvoegen met " @ObjectTextValue = @ObjectTextValue + TEXT_VALUE " niet goed gaat.

Hoe moet ik dit nu aanpakken? Ik heb het e.e.a. nagelezen over table valued parameters waarmee je met meerdere waardes kan goochelen. Maar dat zit weer in een stored procedure en niet een functie.
Verwijderd schreef op woensdag 08 december 2010 @ 14:33:
Dat geeft toch niet? Je hebt toch je OBJECT_KEY? Kun je in T-SQL niet gewoon appenden aan text c.q. kun je in plaats van varchar, waarbij je een lengte moet opgeven, niet gewoon het datatype 'text' gebruiken, waarbij dat niet hoeft en je dus ook niet van te voren hoeft te weten hoe diep alles is.
Maar hoe stel je voor dat ik dan de rijen toevoeg? Ik heb soms SEQNUM 1 t/m 10, maar soms ook 1 t/m 25. En op een onbekende regel staat dan de filename. Ik kan dus niet zomaar 10x of 25 keer een inner join doen en daarmee de text samenvoegen.

Edit: even koffie halen helpt. Ik vermoed dat @ObjectTextValue qua tekstlengte niet goed is...
Edit2: werkt!
DECLARE @ObjectTextValue varchar(max) deed de truuk.

Ik post zo wel even de gehele functie.

Edit 3: complete werkende functie:

SQL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
CREATE FUNCTION dbo.GetObjectTextValue 
( 
    -- Input value in integer
    @ObjectKey int 
) RETURNS nvarchar(500) 
AS 
BEGIN 

-- nvarchar(max) is required as the concatonated string is longer then nvarchar(4000) can hold
DECLARE @ObjectTextValue nvarchar(max)
-- Set initial value to avoid concatting NULL 
SET @ObjectTextValue = '' 

-- Select and concatonate all the TEXT_VALUE's per OBJECT_KEY
SELECT @ObjectTextValue = @ObjectTextValue + TEXT_VALUE 
FROM LANGXMLTEXT t 
-- Limit the query to the objects that are required, the source table contains too many different objects
-- And the query is already a slow query
WHERE OBJECT_KEY = @ObjectKey
-- Sorting is specifically defined as it determines the order how the TEXT_VALUES are put in to @ObjectTextValue
ORDER BY SEQNUM ASC

-- @Position is used to determine where the searchstring is in the complete @ObjectTextValue
-- @Position contains the number of the position where the search string starts, not ends!
-- Bigint to be sure that the position fits
DECLARE @Position bigint 
-- Get filename start 
SET @Position =PATINDEX('%filename="%', @ObjectTextValue)

-- Following statement strips the left side from the search string
SET @ObjectTextValue = 
                       SUBSTRING(
                                 -- String to be stripped
                                 @ObjectTextValue,
                                 -- Determine where the string starts that needs to be kept
                                 -- +10 because the searchstring is 10 characters long...
                                 -- ...and @Position contains the start of the search string, not the end
                                 @Position+10,
                                 -- Determine where the string ends
                                 LEN(@ObjectTextValue)-@Position+11) 

-- Retrieve position of the end quote
SET @Position =PATINDEX('%"%', @ObjectTextValue) 

-- Strip right side , position -1 because the end " does not need to be retrieved
SET @ObjectTextValue = SUBSTRING( @ObjectTextValue, 1,@Position - 1) 

-- Return nvarchar(500)
RETURN @ObjectTextValue 

END;

-- Retrieve Results  
SELECT OBJECT_KEY, dbo.GetObjectTextValue(OBJECT_KEY) AS FileName 
FROM LANGXMLTEXT
WHERE OBJECT_TYPE = 1
AND OBJECT_SUBTYPE = 1


Ik heb nog wel een vraag over het volgende stuk code:
SQL:
1
2
3
4
5
6
7
8
9
SUBSTRING( 
                                 -- String to be stripped 
                                 @ObjectTextValue, 
                                 -- Determine where the string starts that needs to be kept 
                                 -- +10 because the searchstring is 10 characters long... 
                                 -- ...and @Position contains the start of the search string, not the end 
                                 @Position+10, 
                                 -- Determine where the string ends 
                                 LEN(@ObjectTextValue)-@Position+11) 

Waarom is er bij de SUBSTRING() aan het einde het volgende gebruikt:
SQL:
1
LEN(@ObjectTextValue)-@Position+11)

Waarom niet gewoon LEN(@ObjectTextValue) gebruiken? Waarom moet @Position+11 daar afgetrokken? Want eigenlijk gebeurd er het volgende:
LEN(@ObjectTextValue) = Totale lengte van de string
@Position = Start positie van search string filename=", welke 10 karakters lang is

Dan kom je toch uit op het einde van de string weer?

Op mijn te doen lijstje staat nog een poging om de functie flexibeler te maken. Zoeken op andere waarden dan filename=" etc.

In ieder geval hartelijk bedankt, ik heb hier behoorlijk wat van geleerd!

[ Voor 54% gewijzigd door Motrax op 09-12-2010 16:27 ]

☻/
/▌
/ \ Analyseert | Modelleert | Valideert | Solliciteert | Generaliseert | Procrastineert | Epibreert |


Acties:
  • 0 Henk 'm!

  • Motrax
  • Registratie: Februari 2004
  • Niet online

Motrax

Profileert

Topicstarter
En de functie ook aangepast dat er op andere strings kan worden gezocht aan de hand van de query die de functie aanroept. Het is nu behoorlijk flexibel geworden en ik kan hier behoorlijk wat gegevens uit halen.

Staat mijn vraag nog wel open over waarom bij het strippen van de linkerkant van de totale string niet gewoon LEN(@ObjectTextValue) wordt gebruikt.

Volgende stap is om er een Oracle db bij te halen en een omgebouwde functie daarvoor te schrijven aangezien de kans groot is dat de repository op Oracle zit en niet op MS SQL. Met andere woorden, ook daar valt weer wat te leren.

@Skinny, toch nog een vraag. Hoe kreeg je het voor elkaar om die functie foutloos te schrijven? Ben daar eigenlijk wel jaloers op ;)

☻/
/▌
/ \ Analyseert | Modelleert | Valideert | Solliciteert | Generaliseert | Procrastineert | Epibreert |


Acties:
  • 0 Henk 'm!

  • Skinny
  • Registratie: Januari 2000
  • Laatst online: 19-09 16:45

Skinny

DIRECT!

Motrax schreef op vrijdag 10 december 2010 @ 09:57:

@Skinny, toch nog een vraag. Hoe kreeg je het voor elkaar om die functie foutloos te schrijven? Ben daar eigenlijk wel jaloers op ;)
Start-Management Studio-New Query- en typen maar :-)


Over je andere vraag.. ik zie nu inderdaad dat je die @Position+11 er niet af hoeft te trekken. In C# (waar ik in proggel krijg je een fout als je de 3e parameter, de lengte, buiten de input laat vallen maar blijkbaar vind SQL het best en krijg je gewoon het hele restant terug :

SQL:
1
2
3
4
5
6
DECLARE @Input varchar(150)

SET @Input = '01234567890123456789'

SELECT SUBSTRING(@Input,10, LEN(@Input) - 9 )
SELECT SUBSTRING(@Input,10, LEN(@Input))


P.S. dat ik van 10=> 11 heb gemaakt komt omdat de SUBSTRING functie vanaf 1 begint te tellen ipv 0

SIZE does matter.
"You're go at throttle up!"

Pagina: 1