我无法识别网站上使用的所有查询字符串参数。我想编写一个提取所有参数并对其进行计数的T-SQL查询,但我没有编写SQL函数的权限,因此this solution没有多大帮助。
我正在使用的字段(Query
)包含如下所示的数据:
_=1457999955221
tab=profile
tab=tags&sort=votes&page=13
page=5&sort=newest&pagesize=15
...
我需要编写的查询将返回结果:
querystring | count
___________________
_ | 1
tab | 2
sort | 2
page | 2
pagesize | 1
...
非常感谢任何帮助。
答案 0 :(得分:6)
您可以借用其中一个函数from here,并将其内联到查询中。
以下示例。我不希望表现良好。到目前为止,创建CLR函数是在SQL Server 2016之前拆分字符串的最有效方法。
DECLARE @QueryStrings Table
(
Query VARCHAR(8000)
)
INSERT INTO @QueryStrings
VALUES
('INVALID'),
('_=1457999955221'),
('tab=profile'),
('tab=tags&sort=votes&page=13'),
('page=5&sort=newest&pagesize=15');
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),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b)
SELECT parameter, count(*)
FROM @QueryStrings qs
CROSS APPLY (SELECT SUBSTRING(qs.Query, t.N + 1, ISNULL(NULLIF(CHARINDEX('&', qs.Query, t.N + 1), 0) - t.N - 1, 8000))
FROM (SELECT 0
UNION ALL
SELECT TOP (DATALENGTH(ISNULL(qs.Query, 1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM E42) t(N)
WHERE ( SUBSTRING(qs.Query, t.N, 1) = '&'
OR t.N = 0 )) ca1(split_result)
CROSS APPLY (SELECT CHARINDEX('=',split_result)) ca2(pos)
CROSS APPLY (SELECT CASE WHEN pos > 0 THEN LEFT(split_result,pos-1) END,
CASE WHEN pos > 0 THEN SUBSTRING(split_result, pos+1,8000) END
WHERE pos > 0) ca3(parameter,value)
GROUP BY parameter
答案 1 :(得分:2)
更接近性感的方式:
DECLARE @xml xml
;WITH cte AS (
SELECT *
FROM (VALUES
('_=1457999955221'),
('tab=profile'),
('tab=tags&sort=votes&page=13'),
('page=5&sort=newest&pagesize=15')
) as T(Query))
SELECT @xml = (
SELECT CAST(
(
SELECT '<d><param>' + REPLACE(REPLACE((STUFF((
SELECT '/' + REPLACE(REPLACE(Query,'&','/'),'=','!')
FROM cte
FOR XML PATH('')
),1,1,'')),'/','</value><param>'),'!','</param><value>') + '</value></d>') as xml))
;WITH final AS (
SELECT t.v.value('.','nvarchar(20)') as querystring
FROM @xml.nodes('/d/param') as t(v)
)
SELECT querystring, COUNT(*) as [count]
FROM final
GROUP BY querystring
结果:
querystring count
-------------------- -----------
_ 1
page 2
pagesize 1
sort 2
tab 2
(5 row(s) affected)
答案 2 :(得分:0)
不断创新。
现在可以使用SQL Server 2016+(13.x +)轻松完成此操作
-- Required for STRING_SPLIT:
-- ALTER DATABASE <db_name> SET COMPATIBILITY_LEVEL = 130 -- >= 130
BEGIN TRY
DECLARE @sql nvarchar(MAX);
-- https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-database-transact-sql-compatibility-level?view=sql-server-ver15
-- SET @sql = N'ALTER DATABASE [COR_Basic_Demo_V4] SET COMPATIBILITY_LEVEL = 130; ';
SET @sql = N'ALTER DATABASE ' + QUOTENAME(DB_NAME()) + N' SET COMPATIBILITY_LEVEL = ' + (SELECT CAST(MAX(compatibility_level) AS nvarchar(10)) FROM sys.databases) + '; ';
-- PRINT @sql;
EXECUTE(@sql);
END TRY
BEGIN CATCH
-- Execute error retrieval routine.
-- EXECUTE usp_GetErrorInfo;
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
;
END CATCH
-- Here comes the actual computation
DECLARE @input nvarchar(4000)
SET @input = N'_=1457999955221
tab=profile
tab=tags&sort=votes&page=13
page=5&sort=newest&pagesize=15'
;WITH CTE AS
(
SELECT
value
,SUBSTRING(splitted.value, 1, NULLIF(CHARINDEX('=', splitted.value), 0) -1) AS k
,SUBSTRING(splitted.value, NULLIF(CHARINDEX('=', splitted.value), 0) + 1, LEN(splitted.value)) AS v
FROM STRING_SPLIT
(
REPLACE
(
REPLACE(@input, CHAR(13), '')
,CHAR(10)
,'&'
)
, '&'
) AS splitted
)
SELECT
k
,COUNT(v) AS cnt
,COUNT(DISTINCT v) AS dist_cnt
FROM CTE
GROUP BY k
对于早期版本,或者如果您实际上需要分解完整网址,则为:
DECLARE @String nvarchar(4000)
DECLARE @path nvarchar(MAX)
DECLARE @hash nvarchar(MAX)
DECLARE @Delimiter nchar(1)
SET @String = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
SET @String = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET @String = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
SET @Delimiter = '&'
SELECT
@path = SUBSTRING(@String, 1, NULLIF(CHARINDEX('?', @String) - 1, -1)) -- AS path
,@hash = RIGHT(@String, LEN(@String) - NULLIF(CHARINDEX(N'#', @String), 0) ) -- AS hash
SELECT -- remove hash
@String = SUBSTRING
(
@String
,1
,COALESCE(NULLIF(CHARINDEX(N'#', @String), 0) - 1, LEN(@String) )
) -- AS xxx
;
SELECT -- remove path
@String = SUBSTRING
( @String
,CHARINDEX(N'?', @String) + 1
,100000
)
;
;WITH Split(id, stpos, endpos, data)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(@Delimiter, @String) AS endpos
,SUBSTRING(@String, 0, COALESCE(NULLIF(CHARINDEX(@Delimiter, @String), 0), LEN(@String)+1) ) AS data
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(@Delimiter, @String, Split.endpos+1) AS endpos
,SUBSTRING(@String, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(@Delimiter, @String, Split.endpos+1), 0), LEN(@String)+1) - Split.endpos - 1) AS data
FROM Split
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,SUBSTRING(@String, stpos, COALESCE(NULLIF(endpos, 0), LEN(@String)+1) - stpos) AS data_simple
,data
,@path AS path
,@hash AS hash
,SUBSTRING(data, 1, NULLIF(charindex('=', data), 0) -1) AS k
,SUBSTRING(data, NULLIF(charindex('=', data), 0) + 1, LEN(data)) AS v
FROM Split
如果需要功能:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tfu_DecomposeUrl]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
EXECUTE('CREATE FUNCTION dbo.tfu_DecomposeUrl( ) RETURNS TABLE AS RETURN ( SELECT 123 AS abc) ');
GO
ALTER FUNCTION dbo.tfu_DecomposeUrl
(
@input_string NVARCHAR(4000)
)
RETURNS TABLE
AS
RETURN
(
WITH CTE AS
(
SELECT
SUBSTRING(@input_string, 1, NULLIF(CHARINDEX('?', @input_string) - 1, -1)) AS query_path
,RIGHT(@input_string, LEN(@input_string) - NULLIF(CHARINDEX(N'#', @input_string), 0) ) AS query_hash
,SUBSTRING
(
@input_string
,1
,COALESCE(NULLIF(CHARINDEX(N'#', @input_string), 0) - 1, LEN(@input_string) )
) AS PathWithoutHash
)
,CTE2 AS
(
SELECT
CTE.query_path
,CTE.query_hash
,SUBSTRING
( PathWithoutHash
,CHARINDEX(N'?', PathWithoutHash) + 1
,100000
) AS KeyValueString
FROM CTE
)
,Split(id, stpos, endpos, data, query_path, query_hash)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(N'&', CTE2.KeyValueString) AS endpos
,SUBSTRING(CTE2.KeyValueString, 0, COALESCE(NULLIF(CHARINDEX(N'&', CTE2.KeyValueString), 0), LEN(CTE2.KeyValueString)+1) ) AS data
,CTE2.query_path
,CTE2.query_hash
FROM CTE2
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(N'&', CTE2.KeyValueString, Split.endpos+1) AS endpos
,SUBSTRING(CTE2.KeyValueString, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(N'&', CTE2.KeyValueString, Split.endpos+1), 0), LEN(CTE2.KeyValueString)+1) - Split.endpos - 1) AS data
,CTE2.query_path
,CTE2.query_hash
FROM Split
CROSS JOIN CTE2
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,SUBSTRING(@String, stpos, COALESCE(NULLIF(endpos, 0), LEN(@String)+1) - stpos) AS data_simple
,data
,query_path
,query_hash
,SUBSTRING(data, 1, NULLIF(CHARINDEX('=', data), 0) -1) AS k
,SUBSTRING(data, NULLIF(CHARINDEX('=', data), 0) + 1, LEN(data)) AS v
FROM Split
)
GO
这可以简化为
DECLARE @input_string nvarchar(4000)
SET @input_string = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET @input_string = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo#foobar'
-- SET @input_string = 'http://localhost:10004/Kamikatze/ajax/AnySelect.ashx?sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
-- SET @input_string = 'sql=Maps.ObjectBounds.sql&BE_ID=123&obj_uid=fd4ea870-82eb-4c37-bb67-3e8d5b7b7ac2&&in_stichtag=1589528927178&no_cache=1589528927178&no_cache=1589528927178&moo=moo'
;WITH CTE AS
(
SELECT
SUBSTRING(@input_string, 1, NULLIF(CHARINDEX('?', @input_string) - 1, -1)) AS query_path
,RIGHT(@input_string, LEN(@input_string) - NULLIF(CHARINDEX(N'#', @input_string), 0) ) AS query_hash
,SUBSTRING
(
@input_string
,1
,COALESCE(NULLIF(CHARINDEX(N'#', @input_string), 0) - 1, LEN(@input_string) )
) AS PathWithoutHash
)
,CTE2 AS
(
SELECT
CTE.query_path
,CTE.query_hash
,SUBSTRING
( PathWithoutHash
,CHARINDEX(N'?', PathWithoutHash) + 1
,100000
) AS KeyValueString
FROM CTE
)
SELECT
t.id
,t.data
,CTE2.query_path
,CTE2.query_hash
,SUBSTRING(t.data, 1, NULLIF(CHARINDEX('=', t.data), 0) -1) AS k
,SUBSTRING(t.data, NULLIF(CHARINDEX('=', t.data), 0) + 1, LEN(t.data)) AS v
FROM CTE2
OUTER APPLY dbo.tfu_FastSplitString(CTE2.KeyValueString, N'&') AS t
还有一个表值字符串拆分函数:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.tfu_FastSplitString') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
EXECUTE('CREATE FUNCTION dbo.tfu_FastSplitString( ) RETURNS TABLE AS RETURN ( SELECT 123 AS abc) ');
GO
ALTER FUNCTION dbo.tfu_FastSplitString
(
@input_string nvarchar(4000)
,@delimiter nchar(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(id, stpos, endpos) -- , data)
AS
(
SELECT
0 AS id
,0 AS stpos
,CHARINDEX(@delimiter, @input_string) AS endpos
-- ,SUBSTRING(@input_string, 0, COALESCE(NULLIF(CHARINDEX(@delimiter, @input_string), 0), LEN(@input_string)+1) ) AS data
UNION ALL
SELECT
Split.id + 1 AS id
,Split.endpos + 1 AS stpos
,CHARINDEX(@delimiter, @input_string, Split.endpos+1) AS endpos
-- ,SUBSTRING(@input_string, Split.endpos + 1, COALESCE(NULLIF(CHARINDEX(@delimiter, @input_string, Split.endpos+1), 0), LEN(@input_string)+1) - Split.endpos - 1) AS data
FROM Split
WHERE endpos > 0
)
SELECT
id
-- ,stpos
-- ,endpos
-- ,data
,SUBSTRING(@input_string, stpos, COALESCE(NULLIF(endpos, 0), LEN(@input_string)+1) - stpos) AS data
FROM Split
)
GO
这些是内联表值函数,因此它们应该很快。
如果它是一个多语句表值函数,它将很慢。