T-SQL 2008 - получить несколько значений из строки в скобках

У вас не должно быть пробелов в именах столбцов. Имена столбцов должны быть действительными именами переменных для всех сопоставлений столбцов для работы. Лучше не менять имена столбцов, просто иметь отдельный вектор меток, которые вы хотите использовать.

Вот данные с оригинальными именами

shorty_ks <- structure(list(BASEmetab = c(0.11409531425, 0.0716102175833333, 
0.09953587425, 0.248902252416667, 0.114263822125, 0.1530849725
), kn2o.Churchill = c(0.0354781748244595, 0.0511254328535874, 
0.0351401193406382, 0.0432339955562482, 0.0498228392372491, 0.0342448057447306
), kn2o.Oconnor = c(0.0336761898724322, 0.0447691111499001, 0.0333255117582665, 
0.0359191513743014, 0.043530785730249, 0.0324037193153706), kn2o.Owens = c(0.0585830015888074, 
0.10004459351892, 0.0583011578948275, 0.102948639254916, 0.0987018589393615, 
0.0575186770232462)), .Names = c("BASEmetab", "kn2o.Churchill", 
"kn2o.Oconnor", "kn2o.Owens"), row.names = c(NA, 6L), class = "data.frame")

Затем просто создайте вектор из «симпатичных» имен

col_labels <- c(
  kn2o.Churchill="Churchill et al. (1962)", 
  kn2o.Oconnor="OConnor and Dobbins (1958)", 
  kn2o.Owens="Owens (1964)")

Тогда вы можете использовать это для меток в цикле

colNames <- names(shorty_ks)[2:4]
for(i in colNames){
  plt <- ggplot(shorty_ks, aes_string(x=i, y = "BASEmetab")) + 
    xlab(bquote(. (col_labels[i])~k~(h^-1)))+ 
    ylab(expression(BASEmetab~k~(h^-1)))+
    theme_gray()+
    geom_smooth(method="lm",se=FALSE) + geom_point() +
    scale_color_brewer(palette="Dark2")
  print(plt)
}
0
задан Chris Fischer 18 January 2019 в 18:19
поделиться

2 ответа

Основываясь на ответе Хогана, я решил выполнить несколько функций для достижения этой цели.

CREATE FUNCTION [dbo].[GetDerivedDataPointsFromFormula]
(   
@DerivedDataPointId INT
,@strFormula VARCHAR(MAX)
)
RETURNS @RtnValue table
(
id int identity(1,1)
,DerivedDataPointId INT
,DataPointId INT
,Formula VARCHAR(MAX)
)
AS
BEGIN

INSERT INTO @RtnValue(DerivedDataPointId, DataPointId, Formula)
SELECT @DerivedDataPointId
, RTRIM(LTRIM(REPLACE(REPLACE(SUBSTRING(data, 0, CHARINDEX(']', data, 0)),'ST',''), 'INV', ''))) AS DataPointIdInvolved
, @strFormula
FROM (
    SELECT DATA
    FROM dbo.split(@strFormula, '[')
) AS data
WHERE LEN(RTRIM(LTRIM(REPLACE(REPLACE(SUBSTRING(data, 0, CHARINDEX(']', data, 0)),'ST',''), 'INV', '')))) > 0

RETURN

END

Функция разделения определена как:

CREATE FUNCTION [dbo].[Split]
(
@RowData varchar(MAX),
@SplitOn nvarchar(5)
)  
RETURNS @RtnValue table 
(
Id int identity(1,1),
Data nvarchar(1000)
) 
AS  
BEGIN 
Declare @Cnt int
Set @Cnt = 1

While (Charindex(@SplitOn,@RowData)>0)
Begin
    Insert Into @RtnValue (data)
    Select 
        Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1)))

    Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData))
    Set @Cnt = @Cnt + 1
End

Insert Into @RtnValue (data)
Select Data = ltrim(rtrim(@RowData))

Return
END

Тогда я смог получить то, что мне нужно, при использовании Cross Apply

Select DISTINCT f.DerivedDataPointId
, f.DataPointId
,DerivedEval
from DerivedDataPoint d (readuncommitted)
Cross Apply dbo.GetDerivedDataPointsFromFormula(d.DerivedDataPointId, d.DerivedEval) f

Может быть, это поможет кому-то еще искать подобный подход для чего-либо.

0
ответ дан Chris Fischer 18 January 2019 в 18:19
поделиться

Исходя из отправленных вами образцов данных, это очень простая проблема, если извлекаемое вами число:

  1. Длина из восьми цифр

  2. [1110 ]

    Появляется в конце скобки

Если это так, вам просто нужна копия NGrams8K , и вы можете решить эту проблему в 3 строки code:

-- your sample data
DECLARE @string VARCHAR(8000) = 'if ((DateTime.Parse("[ST 35401900]") < DateTime.Parse("[ST 35401903]")) and [35401900]=0 and [35401903]=3, 1, 0)';

-- purely set-based solution using NGrams8K
SELECT ng.position, result = SUBSTRING(ng.token,1,8)
FROM   samd.NGrams8k(@string,9) AS ng
WHERE  CHARINDEX(']',ng.token,8) = 9;

Возвраты:

position   result
---------- --------
26         35401900
60         35401903
78         35401900
95         35401903

Я знаю, вам не нужно знать, где эти числа живут в строке, но я все равно включил их, чтобы продемонстрировать, насколько это легко, если вам нужно.

ОБНОВЛЕНО 22.01.2009 (США) на основе вопросов в комментариях ниже

Для обработки случаев, когда число, которое вы извлекаете, составляет [118 ] не всегда одинаковой длины, вы можете использовать мою функцию patextract8k (которая использует NGrams8K ):

CREATE FUNCTION samd.patExtract8K
(
  @string  VARCHAR(8000),
  @pattern VARCHAR(50)
)
/*****************************************************************************************
[Description]:
 This can be considered a T-SQL inline table valued function (iTVF) equivalent of 
 Microsoft's mdq.RegexExtract: except:

 1. It includes each matching substring's position in the string

 2. It accepts varchar(8000) instead of nvarchar(4000) for the input string, varchar(50)
    instead of nvarchar(4000) for the pattern

 3. The mask parameter is not required and therefore does not exist.

 4. You have specify what text we're searching for as an exclusion; e.g. for numeric 
    characters you should search for '[^0-9]' instead of '[0-9]'. 

 5. There is is no parameter for naming a "capture group". Using the variable below, both 
    the following queries will return the same result:

     DECLARE @string nvarchar(4000) = N'123 Main Street';

   SELECT item FROM samd.patExtract8K(@string, '[^0-9]');
   SELECT clr.RegexExtract(@string, N'(?<number>(\d+))(?<street>(.*))', N'number', 1);

 Alternatively, you can think of patExtract8K as Chris Morris' PatternSplitCM (found here:
 http://www.sqlservercentral.com/articles/String+Manipulation/94365/) but only returns the
 rows where [matched]=0. The key benefit of is that it performs substantially better 
 because you are only returning the number of rows required instead of returning twice as
 many rows then filtering out half of them.

 The following two sets of queries return the same result:

 DECLARE @string varchar(100) = 'xx123xx555xx999';
 BEGIN
 -- QUERY #1
   -- patExtract8K
   SELECT ps.itemNumber, ps.item 
   FROM samd.patExtract8K(@string, '[^0-9]') ps;

   -- patternSplitCM   
   SELECT itemNumber = row_number() over (order by ps.itemNumber), ps.item 
   FROM dbo.patternSplitCM(@string, '[^0-9]') ps
   WHERE [matched] = 0;

 -- QUERY #2
   SELECT ps.itemNumber, ps.item 
   FROM samd.patExtract8K(@string, '[0-9]') ps;

   SELECT itemNumber = row_number() over (order by itemNumber), item 
   FROM dbo.patternSplitCM(@string, '[0-9]')
   WHERE [matched] = 0;
 END;

[Compatibility]:
 SQL Server 2008+

[Syntax]:
--===== Autonomous
 SELECT pe.ItemNumber, pe.ItemIndex, pe.ItemLength, pe.Item
 FROM samd.patExtract8K(@string,@pattern) pe;

--===== Against a table using APPLY
 SELECT t.someString, pe.ItemIndex, pe.ItemLength, pe.Item
 FROM samd.SomeTable t
 CROSS APPLY samd.patExtract8K(t.someString, @pattern) pe;

[Parameters]:
 @string        = varchar(8000); the input string
 @searchString  = varchar(50); pattern to search for

[Returns]:
 itemNumber = bigint; the instance or ordinal position of the matched substring
 itemIndex  = bigint; the location of the matched substring inside the input string
 itemLength = int; the length of the matched substring
 item       = varchar(8000); the returned text

[Developer Notes]:
 1. Requires NGrams8k 

 2. patExtract8K does not return any rows on NULL or empty strings. Consider using 
    OUTER APPLY or append the function with the code below to force the function to return 
    a row on emply or NULL inputs:

    UNION ALL SELECT 1, 0, NULL, @string WHERE nullif(@string,'') IS NULL;

 3. patExtract8K is not case sensitive; use a case sensitive collation for 
    case-sensitive comparisons

 4. patExtract8K is deterministic. For more about deterministic functions see:
    https://msdn.microsoft.com/en-us/library/ms178091.aspx

 5. patExtract8K performs substantially better with a parallel execution plan, often
    2-3 times faster. For queries that leverage patextract8K that are not getting a 
    parallel exeution plan you should consider performance testing using Traceflag 8649 
    in Development environments and Adam Machanic's make_parallel in production. 

[Examples]:
--===== (1) Basic extact all groups of numbers:
  WITH temp(id, txt) as
 (
   SELECT * FROM (values
   (1, 'hello 123 fff 1234567 and today;""o999999999 tester 44444444444444 done'),
   (2, 'syat 123 ff tyui( 1234567 and today 999999999 tester 777777 done'),
   (3, '&**OOOOO=+ + + // ==?76543// and today !!222222\\\tester{}))22222444 done'))t(x,xx)
 )
 SELECT
   [temp.id] = t.id,
   pe.itemNumber,
   pe.itemIndex,
   pe.itemLength,
   pe.item
 FROM        temp AS t
 CROSS APPLY samd.patExtract8K(t.txt, '[^0-9]') AS pe;
-----------------------------------------------------------------------------------------
Revision History:
 Rev 00 - 20170801 - Initial Development - Alan Burstein
 Rev 01 - 20180619 - Complete re-write   - Alan Burstein
*****************************************************************************************/
RETURNS TABLE WITH SCHEMABINDING AS RETURN
SELECT itemNumber = ROW_NUMBER() OVER (ORDER BY f.position),
       itemIndex  = f.position,
       itemLength = itemLen.l,
       item       = SUBSTRING(f.token, 1, itemLen.l)
FROM
(
 SELECT ng.position, SUBSTRING(@string,ng.position,DATALENGTH(@string))
 FROM   samd.NGrams8k(@string, 1) AS ng
 WHERE  PATINDEX(@pattern, ng.token) <  --<< this token does NOT match the pattern
        ABS(SIGN(ng.position-1)-1) +    --<< are you the first row?  OR
        PATINDEX(@pattern,SUBSTRING(@string,ng.position-1,1)) --<< always 0 for 1st row
) AS f(position, token)
CROSS APPLY (VALUES(ISNULL(NULLIF(PATINDEX('%'+@pattern+'%',f.token),0),
  DATALENGTH(@string)+2-f.position)-1)) AS itemLen(l);

Используя PatExtract8K, вы можете легко указать диапазон размеры. Например, допустим, значения могут быть длиной от 7 до 9 цифр. Вы можете сделать это:

-- your sample data
DECLARE @string VARCHAR(8000) = 'if ((DateTime.Parse("[ST 123456789]") < DateTime.Parse("[ST 35401903]")) and [35401900]=0 and [35401903]=3 and [ST 1234567]=x, 1, 0)';

-- Lower and upper bounds for the length of valid values 
DECLARE @low INT = 7, @high INT = 9

SELECT 
  itemIndex  = s.itemIndex, 
  itemLength = s.itemLength-1, 
  item       = SUBSTRING(s.item,0,s.itemLength)
FROM   samd.patExtract8K(REPLACE(@string,']',CHAR(1)),'[^0-9'+CHAR(1)+']') AS s
WHERE  s.itemLength BETWEEN @low AND @high+1;
--AND    SUBSTRING(s.item,0,s.itemLength) NOT LIKE '[^0-9]' <<< If required

Возвращает

itemIndex   itemLength  item
----------- ----------- ------------
26          9           123456789
61          8           35401903
79          8           35401900
96          8           35401903
116         7           1234567

Пара замечаний:

  1. Я обновил образец данные для включения значений длиной 7-9 цифр

  2. Вы должны изменить схему кода на dbo (против samd) или создать схему с именем samd, чтобы использовать эту функцию.

0
ответ дан Alan Burstein 18 January 2019 в 18:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: