I am trying to extract strings from a column to show which are the FROM Tables and JOIN Tables. the complete string consists of one FROM Table and multiple JOIN Tables. Here is a sample of a string:
FROM [TABLEOWNER] .[load_XT_Customer_test] load_XT_Customer_test
INNER JOIN [TABLEOWNER].[load_xt_orders_test] load_xt_orders_test ON load_xt_customer_test.customer_id=load_xt_orders_test.customer_id
INNER JOIN [TABLEOWNER].[load_XT_Orders] load_XT_Orders ON
load_xt_customer_test.customer_id=load_xt_orders.customer_id
INNER JOIN [TABLEOWNER].[load_xt_order_details_test] load_xt_order_details_test
ON load_xt_customer_test.customer_id=load_xt_order_details_test.customer_id
My problem here is that there is no unique character to separate the strings, how can I dynamically extract the single join tables?
I tested it with:
SELECT st_where
,SUBSTRING(st_where
,CHARINDEX('] ',st_where,1)+1
,ABS(CHARINDEX(' ',st_where,CHARINDEX('] ',st_where,1)+1)
-CHARINDEX(']',st_where,1)-1))
AS FromTable
,SUBSTRING(st_where
,CHARINDEX('JOIN [TABLEOWNER].[',st_where,1)+19
,ABS(CHARINDEX(' ',st_where,CHARINDEX('JOIN [TABLEOWNER].[',st_where,1)+19)
-CHARINDEX('JOIN [TABLEOWNER].[',st_where,1)-20))
AS JoinTable
,SUBSTRING(st_where
,CHARINDEX('=',st_where,1)+59
,ABS(CHARINDEX(' ',st_where,CHARINDEX('=',st_where,1)+59)
-CHARINDEX('=',st_where,1)-60))
AS JoinTable2
This should get you started:
DECLARE #st_where nvarchar(max) =
N'
FROM [TABLEOWNER] .[load_XT_Customer_test] load_XT_Customer_test
INNER JOIN [TABLEOWNER].[load_xt_orders_test] load_xt_orders_test ON load_xt_customer_test.customer_id=load_xt_orders_test.customer_id
INNER JOIN [TABLEOWNER].[load_XT_Orders] load_XT_Orders ON
load_xt_customer_test.customer_id=load_xt_orders.customer_id
INNER JOIN [TABLEOWNER].[load_xt_order_details_test] load_xt_order_details_test
ON load_xt_customer_test.customer_id=load_xt_order_details_test.customer_id
';
SELECT
TableName =
SUBSTRING
(
SS.[value],
CharStart.pos + 1,
CharEnd.pos - CharStart.pos + 1
)
FROM STRING_SPLIT
(
-- Replace delimiter phrase with a single character
-- so we can use STRING_SPLIT
-- or use a more general string splitter
REPLACE(#st_where, N'INNER JOIN', NCHAR(256)),
NCHAR(256)
) AS SS
CROSS APPLY
(
VALUES
(
-- Find the start of the table name
CHARINDEX(N'.[', SS.[value], 1)
)
) AS CharStart (pos)
CROSS APPLY
(
VALUES
(
-- Find the end of the table name
CHARINDEX(N']', SS.[value], CharStart.pos)
)
) AS CharEnd (pos);
db<>fiddle demo
The general idea is to split the input string on the phrase "INNER JOIN" then locate the table names using ".[" and "]" as delimiters.
Related
I have a table I want to keep open to sort multiple ways on the fly, so I am inserting into a temp table, but when I try to CONCAT the WHERE statement it can take up to 5 minutes for less then 5k rows ... how would I do a CONCAT to find a string in a CONCAT string and not take forever... if i do a basic select i can return stuff within a few seconds
IF OBJECT_ID('tempdb..#TempTest') IS NOT NULL
DROP TABLE #TempTest
SELECT TOP 1000 *
INTO #TempTest
FROM
(SELECT
hcp_urls_id,
CONCAT (hcp_protocol_text, hcp_protocol_split, hcp_domains_text, hcp_paths_text, '?', hcp_querystrings_text) AS hcp_urls_concat,
hcp_urls_page_statuscode
FROM
hcp_urls
INNER JOIN
hcp_paths ON hcp_paths_id = hcp_urls_paths_id
INNER JOIN
hcp_domains ON hcp_domains_id = hcp_urls_domains_id
INNER JOIN
hcp_protocol ON hcp_protocol_id = hcp_urls_protocol_id
FULL OUTER JOIN
hcp_querystrings ON hcp_querystrings_id = hcp_urls_querystrings_id
WHERE
CONCAT (hhcp_protocol_text, hcp_protocol_split, hcp_domains_text,hcp_paths_text, '?', hcp_querystrings_text) LIKE '%www.test.com/url%') AS tmpTest
I'm trying to do a n inner join by creating a new column using substring to match it with another column in a database so to display the name in the other table
The code is
SELECT
REVERSE(SUBSTRING(REVERSE(flh[FIleName]), 0, CHARINDEX('_', REVERSE(flh[FileName])))) AS FILENAMES,
SUBSTRING(flh.[Filename], 0, LEN(flh.[FileName]) - CHARINDEX('_', REVERSE(flh.[FileName])) + 1) AS DATE,
erc.DisplayName
FROM
[Recon_E].[ETL].[tblFileLoadHistory] flh
INNER JOIN
Feed.dbo.tblEnfusionReRunConfig erc on flh.filenames = erc.FileName
WHERE
flh.RecRunId = (SELECT MAX(RecRunId)
FROM Recon_E.etl.tblFileLoadHistory)
I'm separating the date and the string in the SubString Select.
But I'm not able to give the alias filename to the new column and perform the join
One solution is to use Common Table expressions or CTEs like below if you are on SQL 2005 or later.
;with FileLoadHist as
-- define cte query
(
SELECT REVERSE(SUBSTRING(REVERSE(flh[FIleName]),0,CHARINDEX('_',REVERSE(flh[FileName])) ))AS FILENAMES,
SUBSTRING(flh.[Filename],0, len(flh.[FileName]) - charindex('_',reverse(flh.[FileName])) +1) AS DATE
FROM [Recon_E].[ETL].[tblFileLoadHistory] flh
where flh.RecRunId=( SELECT max(RecRunId) from Recon_E.etl.tblFileLoadHistory )
)
--define outer query
select FILENAMES, DATE, erc.DisplayName
from FileLoadHist
INNER JOIN Feed.dbo.tblEnfusionReRunConfig erc on FileLoadHist.FILENAMES = erc.FileName
SELECT users.first_name, users.last_name, users.email,
(case
when user.AT_ST ='A' then 'Act'
when user.AT_ET = 'E' then 'Est'
end ) as Act_Est
FROM users LEFT OUTER JOIN locations
ON users.id = locations.user_id
WHERE Act_Est IN (' ')
Here this alias name want to use with in same query not creating with other sub queries .
Because if I use other select sub query it fetching first inner sub query and retrieving all the data first and then applying where condition .
So need some help on this .
Use With Statement:
;WITH T AS
(
SELECT
users.first_name,
users.last_name,
users.email,
(case when user.AT_ST ='A' then 'Act' when user.AT_ET = 'E' then 'Est' end ) as Act_Est
FROM users
LEFT OUTER JOIN locations ON users.id = locations.user_id
)
SELECT
*
FROM T
WHERE T.Act_Est IN (' ')
With Statement is a temporary result set that is defined within the execution scope of a single SELECT, INSERT, UPDATE, DELETE, or CREATE VIEW statement
You can use one more SELECT as the below:
SELECT
*
FROM
(
SELECT
users.first_name,
users.last_name,
users.email,
(case
when user.AT_ST ='A' then 'Act'
when user.AT_ET = 'E' then 'Est'
end ) as Act_Est
FROM
users LEFT OUTER JOIN
locations ON users.id = locations.user_id
) A
WHERE
A.Act_Est IN (' ')
To have intermediate variables for futher calculations use CROSS APPLY
SELECT users.first_name, users.last_name, users.email, vars.Act_Est
FROM users
CROSS APPLY (
SELECT case
when users.AT_ST ='A' then 'Act'
when users.AT_ET = 'E' then 'Est'
end as Act_Est) vars
LEFT OUTER JOIN locations
ON users.id = locations.user_id
WHERE vars.Act_Est IN (' ')
Actually all the above options are taking more time .
My actual query is like below .
When ( T.co in ('ps', 'pi', 'ri')) then ('PFG')
When ( T.co in ('qs', 'qb')) then ('QBE')
When ( T.co in ('sr')) then ('OTH')
When ( T.co in ('rg', 'qi')) then ('EX2')
When ( T.co in ('LT', 'SL', 'ST')) then ('EX3')
End ) as Company_Group ,
Here I want to use in where clause Company group directly .IF I use any with clause or putting this in sub query it is executing the above query entirely and executing where clause .so it taking lot of time So my requirment how I can directly pass my where condition for aliase columns in this query .
below is T-SQL generated by application
DECLARE #xml_0 XML
SET #xml_0 = N'<Val>8e4cd3e3-de98-4f55-9c55-57881157a0f0</Val>
<Val>2f483275-7333-4786-aca8-454e5bf4823f</Val>
<Val>ce1ce763-1f68-48ec-bedf-f4641e40d8f8</Val>
<Val>6b471d5e-fd5c-4db8-aa31-abb910651e18</Val>
<Val>89064e42-0592-4845-b21e-38f788ab0d2e</Val>
<Val>d54793f0-cbfb-428e-ba08-db70cab1af07</Val>
<Val>8027e6bd-09e5-4a5b-aae7-54aff4a0e6c0</Val>
<Val>53f1a5e3-b2a8-49c3-935b-a5ac7fe0c1d8</Val>
<Val>faceabad-1d0c-4f3f-8d94-674bbf1c3428</Val>
<Val>f8e0a43d-cff7-45aa-b73f-6858b1d17cd1</Val>
<Val>94e9bc76-5bb3-4cf9-9b59-fc3163c904d7</Val>
<Val>e4be8c69-5166-40cc-b49a-18adec78e356</Val>
<Val>5c564b82-64e1-46c5-a41d-bc30104f14a5</Val>
<Val>dc246c2c-7edd-407a-b378-747789bd5a75</Val>
<Val>411ac1e9-3d4f-447c-808a-b82d388816dd</Val>'
SELECT COUNT(*) FROM
(
SELECT [t0].[ID]
FROM [dbo].[HM_Rows] AS [t0], [dbo].[HM_Cells] AS [t1]
WHERE
(
[t1].[Value] IN
(
SELECT node.value('.', 'NVARCHAR(200)') FROM #xml_0.nodes('/Val') xml_0(node)
)
)
) AS [r2487772634]
here is execution plan of T-SQL above
so it scans index
it scans correct indexes
missing_index_FOR_Value_INC_RowID - on HM_Cells table
and
PK_HM_Rows - on HM_Rows table
any idea?
P.S tables are large
Row counts
HM_Rows - 17'736'181
HM_Cells - 1'048'693'775
AND YES i have rebuilded indexes and updated statistics
HM_Cells.Value is NVarChar(200)
also without XML and HM_Rows table it working fine
e.g
SELECT ID FROM HM_Cells WHERE Value IN (.........)
works excellent
Thanks a lot :)
Try using a JOIN instead of an IN, because this way you can force a loop strategy, which will probably use a seek instead of a scan:
SELECT COUNT(*) FROM
(
SELECT [t0].[ID]
FROM (
SELECT DISTINCT node.value('.', 'NVARCHAR(200)') AS Val
FROM #xml_0.nodes('/Val') xml_0(node)
) q1 INNER LOOP JOIN [dbo].[HM_Cells] AS [t1] ON q1.Val=t1.Value
CROSS JOIN [dbo].[HM_Rows] AS [t0]
) AS [r2487772634]
Is it possible to do the following:
IF [a] = 1234 THEN JOIN ON TableA
ELSE JOIN ON TableB
If so, what is the correct syntax?
I think what you are asking for will work by joining the Initial table to both Option_A and Option_B using LEFT JOIN, which will produce something like this:
Initial LEFT JOIN Option_A LEFT JOIN NULL
OR
Initial LEFT JOIN NULL LEFT JOIN Option_B
Example code:
SELECT i.*, COALESCE(a.id, b.id) as Option_Id, COALESCE(a.name, b.name) as Option_Name
FROM Initial_Table i
LEFT JOIN Option_A_Table a ON a.initial_id = i.id AND i.special_value = 1234
LEFT JOIN Option_B_Table b ON b.initial_id = i.id AND i.special_value <> 1234
Once you have done this, you 'ignore' the set of NULLS. The additional trick here is in the SELECT line, where you need to decide what to do with the NULL fields. If the Option_A and Option_B tables are similar, then you can use the COALESCE function to return the first NON NULL value (as per the example).
The other option is that you will simply have to list the Option_A fields and the Option_B fields, and let whatever is using the ResultSet to handle determining which fields to use.
This is just to add the point that query can be constructed dynamically based on conditions.
An example is given below.
DECLARE #a INT = 1235
DECLARE #sql VARCHAR(MAX) = 'SELECT * FROM [sourceTable] S JOIN ' + IIF(#a = 1234,'[TableA] A ON A.col = S.col','[TableB] B ON B.col = S.col')
EXEC(#sql)
--Query will be
/*
SELECT * FROM [sourceTable] S JOIN [TableB] B ON B.col = S.col
*/
You can solve this with union
select a, b
from tablea
join tableb on tablea.a = tableb.a
where b = 1234
union
select a, b
from tablea
join tablec on tablec.a = tableb.a
where b <> 1234
I disagree with the solution suggesting 2 left joins. I think a table-valued function is more appropriate so you don't have all the coalescing and additional joins for each condition you would have.
CREATE FUNCTION f_GetData (
#Logic VARCHAR(50)
) RETURNS #Results TABLE (
Content VARCHAR(100)
) AS
BEGIN
IF #Logic = '1234'
INSERT #Results
SELECT Content
FROM Table_1
ELSE
INSERT #Results
SELECT Content
FROM Table_2
RETURN
END
GO
SELECT *
FROM InputTable
CROSS APPLY f_GetData(InputTable.Logic) T
I think it will be better to think about your query in a different way and treat them more like sets.
I do believe if you make two separate queries then join them using UNION, It will be much better in performance and more readable.