2012-03-16 2 views
7

입력 문자열에 '1,5,6'및 '2,89,9'와 같은 요소 수가 3 또는 plus 인 2 개의 문자열이 있습니다. 사람들이 문자열은 나는 내가 ROWNUMBER를 할당 생각했다SQL Server 가입 주문

1 2 
5 89 
6 9 

으로 "가입 좌표"만들고있는이

SELECT a.item, b.item FROM 
    (
    SELECT 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS rownumber, 
    * FROM dbo.Split('1,5,6',',') 
) AS a 
    INNER JOIN 
    (
    SELECT 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS rownumber, 
    * FROM dbo.Split('2,89,9',',') 
) AS b ON a.rownumber = b.rownumber 

로 설정 2 결과 사이의 조인을 만들어 원하는하는 가장 좋은 방법 이제까지?

답변

13

dbo.Split()이 데이터 세트를 리턴 할 때, 사용자가 원하는 행 번호를 절대 확실성과 함께 (문자열의 순서에 따라) 할당 할 수 없습니다. SQL 절대 은 실제로 데이터와 관련된 ORDER BY이없는 주문을 보장합니다. 당신과 함께

트릭 주문 (SELECT 0)를 사용에 의해 당신이 할 수있다 자주 올바른 값을 얻을. 아마 매우 일 것입니다. 그러나 이것은 결코보장입니다. 가끔씩 당신 이됩니다.

dbo.Split()을 다시 코딩하여 문자열이 구문 분석 될 때 row_number를 할당하는 것이 가장 좋습니다. 그런 다음에 만 row_number가 실제로 목록의 항목 위치에 해당하는지 100 % 확실하게 알 수 있습니다.

그런 다음 제안에 따라 참여하고 원하는 결과를 얻습니다. 그 외에는


, 아이디어는 나에게 잘 보인다. 비록 한 목록이 다른 목록보다 길 수 있다면 FULL OUTER JOIN을 고려할 수도 있습니다.

+0

네 말이 맞습니다. 그리고 그것은 모범 사례입니다.) 그러나 특정 "ORDER JOIN"이 있다고 생각했습니다 :-) –

+0

예, SELECT 명령문이 원하는 순서로 탬퍼하는 비슷한 경우는 순서화 된 파생 테이블에서 선택하는 것입니다 : http://stackoverflow.com/ q/18961789/521799 –

7

는이처럼 분할 기능을뿐만 아니라

같이 그것을 고려 할 수 있습니다

CREATE FUNCTION Split 
(
    @delimited nvarchar(max), 
    @delimiter nvarchar(100) 
) RETURNS @t TABLE 
(
    id int identity(1,1), 
    val nvarchar(max) 
) 
AS 
BEGIN 
    declare @xml xml 
    set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>' 

    insert into @t(val) 
    select 
    r.value('.','varchar(5)') as item 
    from @xml.nodes('//root/r') as records(r) 

    RETURN 
END 
GO 

가 함께 JOIN 그들에게 간단한 작업을 할 것이다. 이처럼 :

SELECT 
    * 
FROM 
    dbo.Split('1,5,6',',') AS a 
    JOIN dbo.Split('2,89,9',',') AS b 
     ON a.id=b.id 

이의 상승은 성능이 재귀 분할 기능을 더 코멘트에서와 같이 어떤 ROW_NUMBER() OVER(ORDER BY SELECT 0)

편집

를 필요가 없다는 것입니다. 그래서 아마 이런 식으로 뭔가 :

CREATE FUNCTION dbo.Split (@s varchar(512),@sep char(1)) 
RETURNS table 
AS 
RETURN (
    WITH Pieces(pn, start, stop) AS (
     SELECT 1, 1, CHARINDEX(@sep, @s) 
     UNION ALL 
     SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1) 
     FROM Pieces 
     WHERE stop > 0 
    ) 
    SELECT pn, 
     SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s 
    FROM Pieces 
) 
GO 

그리고 그 선택은 다음과 같이이다 : 아리온의 제안에

SELECT 
    * 
FROM 
    dbo.Split('1,5,6',',') AS a 
    JOIN dbo.Split('2,89,9',',') AS b 
     ON a.pn=b.pn 
+0

좋은 소식! ;) thanks –

+1

아무런 문제가 없습니다 .. 도와 주니 기꺼이 – Arion

+1

@Arion -이 접근법은 *** 레코드가 삽입되는 순서를 보장합니다. 필자의 이해는 parrallel 처리 등과 같은 고려 사항이 여기에 적용됩니다. ORDER BY를 지정하지 않았으므로 데이터가 나타날 순서와 다른 순서로 삽입 될 수 있습니다. 문자열에. 문자열을 반복적으로 반복하고 명시 적으로 ID 값을 직접 작성하는 방법을 선호하기 때문에 이러한 보증을 제공하는 참조가 있으면이를 볼 수 있습니다. 난 그저 *** 보증 *** :) – MatBailie

0

감사합니다.그것은 나를 위해 매우 유용합니다. 필자는 varchar (max) 유형의 입력 문자열을 지원하기 위해 함수를 약간 수정했으며, 구분 기호 문자열의 최대 길이는 1000입니다. 또한 최종 반환시 빈 문자열이 필요한지 여부를 나타내는 매개 변수가 추가되었습니다.

MatBailie의 질문에 대해서는 인라인 함수이기 때문에이 함수를 호출하는 외부 쿼리에 pn 열을 포함시킬 수 있습니다.

CREATE FUNCTION dbo.Split (@s nvarchar(max),@sep nvarchar(1000), @IncludeEmpty bit) 
RETURNS table 
AS 
RETURN (
    WITH Pieces(pn, start, stop) AS (
     SELECT convert(bigint, 1) , convert(bigint, 1), convert(bigint,CHARINDEX(@sep, @s)) 
     UNION ALL 
     SELECT pn + 1, stop + LEN(@sep), CHARINDEX(@sep, @s, stop + LEN(@sep)) 
     FROM Pieces 
     WHERE stop > 0 
    ) 
    SELECT pn, 
     SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE LEN(@s) END) AS s 
    FROM Pieces 
    where start< CASE WHEN stop > 0 THEN stop ELSE LEN(@s) END + @IncludeEmpty 
) 

그러나 반환 할 목록에 100 개가 넘는 레코드가있는 경우이 함수를 사용하여 약간 문제가 발생했습니다. 그래서, 순수하게 문자열 해석 함수를 사용하여 또 다른 함수를 만들었습니다 :

Create function [dbo].[udf_split] (
    @ListString nvarchar(max), 
    @Delimiter nvarchar(1000), 
    @IncludeEmpty bit) 
Returns @ListTable TABLE (ID int, ListValue varchar(max)) 
AS 
BEGIN 
    Declare @CurrentPosition int, @NextPosition int, @Item nvarchar(max), @ID int 
    Select @ID = 1, 
      @ListString = @Delimiter+ @ListString + @Delimiter, 
      @CurrentPosition = 1+LEN(@Delimiter) 

    Select @NextPosition = Charindex(@Delimiter, @ListString, @CurrentPosition) 
    While @NextPosition > 0 Begin 

     Select @Item = Substring(@ListString, @CurrentPosition, @[email protected]) 
     If  @IncludeEmpty=1 or Len(LTrim(RTrim(@Item)))>0 Begin 
       Insert Into @ListTable (ID, ListValue) Values (@ID, LTrim(RTrim(@Item))) 
       Set @ID = @ID+1 
     End 
     Select @CurrentPosition = @NextPosition+LEN(@Delimiter), 
       @NextPosition = Charindex(@Delimiter, @ListString, @CurrentPosition) 
    End 
    RETURN 
END 

희망이 도움이 될 것입니다.