sql 字符串随地吐痰,数字表,理货表,

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sql 字符串随地吐痰,数字表,理货表,相关的知识,希望对你有一定的参考价值。

/********************************
INTRO TO SPLITTING STRINGS
********************************/

/*
Shows methods for splitting demilited lists of values. 

These examples use the following comma-delimited list:

'cat,dog,mouse  , bird,giraffe,fish,shark,skunk,sloth,owl,porcupine,wolf,bush baby,ant'

See this article about theory behind tally tables: http://www.sqlservercentral.com/articles/T-SQL/62867/

The method behind a Numbers or Tally Table is nothing more than a table with a single column of 
very well indexed sequential numbers starting at 0 or 1 (mine start at 1) and going up to some 
number. The largest number in the Tally table should not be just some arbitrary choice. It should 
be based on what you think you'll use it for. I split VARCHAR(8000)'s with mine, so it has to be 
at least 8000 numbers.

*/

/********************************
METHOD 1 - USING CTEs AND CROSS JOINING TO CREATE NUMBERS TABLE
********************************/

/*
This is a CSV splitter based on a single character delimiter, apparently with 15-20% improved performance.

Below, the CTE called E1 (as in 10E1 for scientific notation) is nothing more than ten SELECT 1's returned 
as a single result set.

E2 does a CROSS JOIN of E1 with itself. That returns a single result set of 10*10 or up to 100 rows. 
I say "up to" because if the TOP function is 100 or less, the CTE's are "smart" enough to know that 
it doesn't actually need to go any further and E4 and E8 won't even come into play. If the TOP has 
a value of less than 100, not all 100 rows that E2 is capable of making will be made. It'll always 
make just enough according to the TOP function.

You can follow from there. E4 is a CROSS JOIN of E2 and will make up to 100*100 or 10,000 rows 
and E8 is a CROSS JOIN of E4 which will make more rows than most people will ever need. If you do 
need more, then just add an E16 as a CROSS JOIN of E8 and change the final FROM clause to FROM E16.

What's really amazing about this bad-boy is that is produces ZERO READS. Absolutely none, nada, nil.

See http://www.sqlservercentral.com/articles/Tally+Table/72993/

*/

CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
        (@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
     -- enough to cover VARCHAR(8000)
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                   FROM cteStart s
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
        Item       = SUBSTRING(@pString, l.N1, l.L1)
   FROM cteLen l
;

/*
Results will be:

The final results will be:

ItemNumber	Item
1			cat
2			dog
3			mouse  
4			 bird
5			giraffe
6			fish
7			shark
8			skunk
9			sloth
10			owl
11			porcupine
12			wolf
13			bush baby
14			ant
*/

-- DETAIL OF THIS EXAMPLE!!!


CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
        (
	-- FOR EASE OF TESTING PURPOSES, DECLARING VARIABLES OUTSIDE OF FUNCTION
	declare @pString VARCHAR(8000), @pDelimiter CHAR(1)
	-- FOR THIS EXAMPLE, SET THE @pString value to be split as:
	set @pString = 'cat,dog,mouse  , bird,giraffe,fish,shark,skunk,sloth,owl,porcupine,wolf,bush baby,ant'
	set @pDelimiter = ','
	--)
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE!  IT WILL KILL PERFORMANCE!
--RETURNS TABLE WITH SCHEMABINDING AS
 --RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
     -- enough to cover VARCHAR(8000)
	 -- starts with CTE named E1 with column N, selects 10 rows, then CROSS JOINs E1 and E1 returning 100 rows,
	 -- then CROSS JOINs E2 and E2 returning 10,000 rows.
	 -- if you need more rows, you can create E8 to get 100,000,000 rows 
	 -- the CTEs are in sequence, and therefore each CTE can reference the CTEs that came before
  -- create CTE E1 with column N which has value of 1 in 10 rows
  ;WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ), --10E+1 or 10 rows
	   -- create CTE E2 with column N which has value of 1 in 100 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --returns 10E+2 or 100 rows, same as SELECT 1 FROM E1 a CROSS JOIN E1 b
       -- create CTE E4 with column N which has value of 1 in 10,000 rows
	   E4(N) AS (SELECT 1 FROM E2 a, E2 b), -- returns 10E+4 or 10,000 rows max, same as SELECT 1 FROM E2 a CROSS JOIN E2 b
 -- NEXT, cteTally sets the "base CTE and limits the number of rows based on the string length of @pString
 -- for example, E4 is set up to return 10,000 rows max, but if your @pString value only 1,472 characters, 
 -- SQL Server is smart enough to limit the total output to 1,472. 
 -- This is done by plugging the string length into a TOP clause. 
 /*
Results of this cteTally will be (since there are 85 characters in @pString:

N
1
2
3
...
83
84
85

*/
 cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
-- NEXT, creates cteStart, creates table with starting position value for each 'element' following the delimiter
-- starts with 1 in row 1, then row 2 has the first position after the first delimiter, row 3 has first position after second delimiter, etc.
/*
Results of this cteStart will be (for instance, first comma is character 4 and therefore 'dog'
begins on character 5, as is shown in row 2):

N1
1
5
9
17
23
31
36
42
48
54
58
68
73
83

*/
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT 1 UNION ALL
                 SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
                ),
-- NEXT, creates cteLEN with columns N1 and L1 that shows start position and length of each element in the list.
-- N1 returns starting position of each element
-- L1 returns the length of that element
-- the final row's L1 is placed in as a hard coded 8000
/*
Results of this cteLen will be (for instance, first comma is character 4 and therefore 'dog'
begins on character 5, and has a length of 3 characters, as is shown in row 2):

N1	L1
1	3
5	3
9	7
17	5
23	7
31	4
36	5
42	5
48	5
54	3
58	9
68	4
73	9
83	8000

*/
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
                 SELECT s.N1,
                        ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
                   FROM cteStart s
                ) 
--===== NEXT, do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
-- returns two rows, ItemNumber and Item (or element)
-- ItemNumber simply counts the numbers rows, thereby giving a number each element within the comma delimited list
	-- it does this by using the ROW_NUMBER() on the N1 column from cteLen 
-- Item returns the separated value of each element. 
	-- It does this using the starting position from N1 and the length from L1 in cteLen

 SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY a.N1),
        Item       = SUBSTRING(@pString, a.N1, a.L1)
   FROM cteLen a
;

/*

The final results will be:

ItemNumber	Item
1			cat
2			dog
3			mouse  
4			 bird
5			giraffe
6			fish
7			shark
8			skunk
9			sloth
10			owl
11			porcupine
12			wolf
13			bush baby
14			ant

*/

/********************************
BASIC EXAMPLE
********************************/

/*
Below, the CTE called E1 (as in 10E1 for scientific notation) is nothing more than ten SELECT 1's returned 
as a single result set.

E2 does a CROSS JOIN of E1 with itself. That returns a single result set of 10*10 or up to 100 rows. 
I say "up to" because if the TOP function is 100 or less, the CTE's are "smart" enough to know that 
it doesn't actually need to go any further and E4 and E8 won't even come into play. If the TOP has 
a value of less than 100, not all 100 rows that E2 is capable of making will be made. It'll always 
make just enough according to the TOP function.

You can follow from there. E4 is a CROSS JOIN of E2 and will make up to 100*100 or 10,000 rows 
and E8 is a CROSS JOIN of E4 which will make more rows than most people will ever need. If you do 
need more, then just add an E16 as a CROSS JOIN of E8 and change the final FROM clause to FROM E16.

What's really amazing about this bad-boy is that is produces ZERO READS. Absolutely none, nada, nil.
*/

DECLARE @N INT = 96

;WITH E1(N) AS( -- 10 ^ 1 = 10 rows
    SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b), -- 10 ^ 8 = 10,000,000 rows
CteTally(N) AS(
    SELECT TOP(@N) ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
    FROM E8
)    
SELECT * FROM CteTally ORDER BY N DESC

/*

Result: a table with a single column 'N' which contains number going up to the value of @N

N
96
95
94
93
92
91
...
5
4
3
2
1


*/



/********************************
METHOD 2 - FT_SPLIT_STRING FROM TESSITURA, MODIFIED VERISON OF METHOD ABOVE
********************************/

USE [Impresario]
GO

/****** Object:  UserDefinedFunction [dbo].[FT_SPLIT_LIST]    Script Date: 10/29/2018 3:10:33 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


CREATE FUNCTION [dbo].[FT_SPLIT_LIST] (@pString VARCHAR(8000), @pDelimiter CHAR(1))

/**************************************************************************
DESCRIPTION: Accepts a delimited string and splits it at the specified
delimiter points. Returns the individual items as a table data
type with the ElementID field as the array index and the Element
field as the data
PARAMETERS:
@vcDelimitedString - The string to be split
@vcDelimiter - String containing the delimiter where
delimited string should be split
RETURNS:
Table data type containing array of strings that were split with
the delimiters removed from the source string
USAGE:
SELECT ElementID, Element FROM FT_SPLIT_LIST('11111,22222,3333', ',') ORDER BY ElementID

Modified 6/14/07 MAR FP67. Changed @vcDelimitedString from nVarChar to VarChar
	so that the size could be increased to 8000
Modified 7/17/07 SEB. Changed @vcDelimiter from nVarchar to Varchar
    so that it works when parsing the old password history string
Modified CWR 4/5/2010 FP1556. so that this uses a CTE number table instead of looping (much faster)
Modified CWR 5/21/2010 FP1556. rewritten as an inline function
Modified CWR 3/1/2011 FP1859. so that the function can deal with 8000 characters
Modified CWR 5/2/2011 with newly optimized code found online (http://www.sqlservercentral.com/articles/Tally+Table/72993/)
	that gets rid of table reads

***************************************************************************/

RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
     -- enough to cover VARCHAR(8000)
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT 0 UNION ALL
                 SELECT TOP (DATALENGTH(ISNULL(@pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT t.N+1
                   FROM cteTally t
                  WHERE (SUBSTRING(@pString,t.N,1) = @pDelimiter OR t.N = 0) 
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ElementId = convert(smallint, ROW_NUMBER() OVER(ORDER BY s.N1)),
        Element = convert(nvarchar(1200), SUBSTRING(@pString,s.N1,ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)))
   FROM cteStart s
;

GO


-- DETAIL OF THE METHOD ABOVE!!!

CREATE FUNCTION [dbo].[FT_SPLIT_LIST] (

-- FOR EASE OF TESTING PURPOSES, DECLARING VARIABLES OUTSIDE OF FUNCTION
declare @pString VARCHAR(8000), @pDelimiter CHAR(1)
--)
	-- FOR THIS EXAMPLE, SET THE @pString value to be split as:
	set @pString = 'cat,dog,mouse  , bird,giraffe,fish,shark,skunk,sloth,owl,porcupine,wolf,bush baby,ant'
	set @pDelimiter = ','


RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
     -- enough to cover VARCHAR(8000)
  WITH E1(N) AS (
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
       E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
       E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
 cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
                     -- for both a performance gain and prevention of accidental "overruns"
                 SELECT 0 UNION ALL
                 SELECT TOP (DATALENGTH(ISNULL(@pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                 SELECT t.N+1
                   FROM cteTally t
                  WHERE (SUBSTRING(@pString,t.N,1) = @pDelimiter OR t.N = 0) 
                )
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
 SELECT ElementId = convert(smallint, ROW_NUMBER() OVER(ORDER BY s.N1)),
        Element = convert(nvarchar(1200), SUBSTRING(@pString,s.N1,ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)))
   FROM cteStart s
;

GO


/********************************
METHOD 3 - USING SQL SERVER'S SPLIT_STRING FUNCTION
********************************/

/*
SQL Server has a built-in function called STRING_SPLIT(), but it is only available if the database
has a compatability level of 130 or higher! Due to this, it may be a better decision to stick to
a method that is not dependent on the compatability level of the target database.

STRING_SPLIT returns a single column that has a column header of Value. You set up the function
by SELECTing the Value column FROM STRING_SPLIT, plugging in the variable the holds the string
that you're splitting.
*/

DECLARE @tags NVARCHAR(400) = 'clothing,road,,touring,bike'  

SELECT Value  
FROM STRING_SPLIT(@tags, ',')  
WHERE RTRIM(value) <> '';  --optional RTRIM, removes empty strings that are between delimiters

/*
Results:

Value
clothing
road
touring
bike

*/

/*
You can even JOIN off of SPLIT_STRING. For example, say you want to pass Campaign values from
a comma-delimited list into a single row of tabular data so that you can then INNER JOIN that table
against a Contributions table and therefore get only Contribution rows that match the Campaign
values from the comma-delimited list.
*/

DECLARE @IDs NVARCHAR(400) = '27,52,6441,646461'  

SELECT a.OrderID, a.OrderDate,  a.Customer_No
FROM STRING_SPLIT(@IDs, ',') AS b -- treating STRING_SPLIT as a virtual table
INNER JOIN Orders a ON a.OrderID = CAST(b.Value AS INT); -- JOINing ON the STRING_SPLIT table

以上是关于sql 字符串随地吐痰,数字表,理货表,的主要内容,如果未能解决你的问题,请参考以下文章

需要一个查询来从所有表/视图中搜索一个值(“数字”)

DB2中字符转数字的问题

SQL中怎么设置字段为10位数字。

oracle建表语句

SQL,数字辅助表

数据库的优化(表优化和sql语句优化)