Dynamic SQL statement with nested selects - sql

I have two tables, transforms and design_fields. transforms has a field called transforms, and design_fields has a field called transform. design_fields contains all of the transform rows, in which some are duplicates. transforms (table) transform (column) is what I am trying to populate with the transform values from design_fields.
I've been doing some of them by hand using the following queries.
Get list of design_fields transform that are not in transforms (table) transform (column):
select transform, COUNT(*) as trans_count
from design_fields
where transform not in (
select transform
from transforms
where transform is not null
)
group by transform
order by trans_count desc
Insert design_field transform into transforms (table) transform (column) + other stuff.
insert into transforms (field_id, transform_name, transform)
select field_id, logical_name, 'TRANSFORM NAME GOES HERE' as transform
from fields
where field_id in (
select field_id
from design_fields
where transform = 'TRANSFORM NAME GOES HERE'
)
Though this method works for the most part, there are over 600 rows that I would need to do this in. This is why I'd like to use a dynamic query to insert all the rows from that listing into the transforms table.
Here is what I have so far (disclaimer: I'm new to dynamic queries):
select 'insert into transforms (field_id, transform_name, transform)
select field_id, logical_name, ' +
(select distinct transform
from design_fields
where transform not in (
select transform
from transforms
where transform is not null ))
+ ' as transform
from fields
where field_id in (
select field_id
from design_fields
where transform = ' +
(select distinct transform
from design_fields
where transform not in (
select transform
from transforms
where transform is not null )) +
')'
This statement returns an error message saying that it returned more than 1 value. I understand why this is, I just don't know any other way to do this.
Any help, including pointers for future use of dynamic queries would be greatly appreciated!

What about using EXEC statement where you can make its parameter a string.
The exec statement would call a stored proc passing the insert parameters you want. for example:
Declare #SQL varchar(600);
Set #SQL = 'select * from yourTable';
EXEC(#SQL);

Related

Checking if field contains multiple string in sql server

I am working on a sql database which will provide with data some grid. The grid will enable filtering, sorting and paging but also there is a strict requirement that users can enter free text to a text input above the grid for example
'Engine 1001 Requi' and that the result will contain only rows which in some columns contain all the pieces of the text. So one column may contain Engine, other column may contain 1001 and some other will contain Requi.
I created a technical column (let's call it myTechnicalColumn) in the table (let's call it myTable) which will be updated each time someone inserts or updates a row and it will contain all the values of all the columns combined and separated with space.
Now to use it with entity framework I decided to use a table valued function which accepts one parameter #searchQuery and it will handle it like this:
CREATE FUNCTION myFunctionName(#searchText NVARCHAR(MAX))
RETURNS #Result TABLE
( ... here come columns )
AS
BEGIN
DECLARE #searchToken TokenType
INSERT INTO #searchToken(token) SELECT value FROM STRING_SPLIT(#searchText,' ')
DECLARE #searchTextLength INT
SET #searchTextLength = (SELECT COUNT(*) FROM #searchToken)
INSERT INTO #Result
SELECT
... here come columns
FROM myTable
WHERE (SELECT COUNT(*) FROM #searchToken WHERE CHARINDEX(token, myTechnicalColumn) > 0) = #searchTextLength
RETURN;
END
Of course the solution works fine but it's kinda slow. Any hints how to improve its efficiency?
You can use an inline Table Valued Function, which should be quite a lot faster.
This would be a direct translation of your current code
CREATE FUNCTION myFunctionName(#searchText NVARCHAR(MAX))
RETURNS TABLE
AS RETURN
(
WITH searchText AS (
SELECT value token
FROM STRING_SPLIT(#searchText,' ') s(token)
)
SELECT
... here come columns
FROM myTable t
WHERE (
SELECT COUNT(*)
FROM searchText
WHERE CHARINDEX(s.token, t.myTechnicalColumn) > 0
) = (SELECT COUNT(*) FROM searchText)
);
GO
You are using a form of query called Relational Division Without Remainder and there are other ways to cut this cake:
CREATE FUNCTION myFunctionName(#searchText NVARCHAR(MAX))
RETURNS TABLE
AS RETURN
(
WITH searchText AS (
SELECT value token
FROM STRING_SPLIT(#searchText,' ') s(token)
)
SELECT
... here come columns
FROM myTable t
WHERE NOT EXISTS (
SELECT 1
FROM searchText
WHERE CHARINDEX(s.token, t.myTechnicalColumn) = 0
)
);
GO
This may be faster or slower depending on a number of factors, you need to test.
Since there is no data to test, i am not sure if the following will solve your issue:
-- Replace the last INSERT portion
INSERT INTO #Result
SELECT
... here come columns
FROM myTable T
JOIN #searchToken S ON CHARINDEX(S.token, T.myTechnicalColumn) > 0

Incorrect Results in my SQL select Query when parsing XML text column

Hi all I have a table that holds my business Id's and it is varchar(255) data type
I also have a separate table that stores an XML structured document in a text data type column when the business gets approved by a lender (it stores the companys information etc).
I am trying to return all business ID's that are NOT approved by a lender, the only way i can know this is if the business ID does not exist in the XML.
I cannot join on any tables as i do not have any relational data, but i am trying to subquery it.
Any ideas? here is what i have
Select bus_id
From dbo.tbl_business
Where bus_id Not In (
Select Cast(company_xml_info As Varchar(Max))
From tbl_company_reports
Where Cast(company_xml_info As Varchar(Max)) Is Not Null
And company_xml_info Like '%Business id="' + bus_id + '"%'
And company_xml_info Is Not Null
And company_xml_current_status = 'Approved'
)
Here is an example mark of something similar you can do. This should run fine in SQL Management Studio 2008 and up:
DECLARE #Data TABLE (BusinessId VARCHAR(8))
INSERT INTO #Data (BusinessId) VALUES ('A68'),('A69'),('A70');
DECLARE #CompanyXml TABLE (company_xml_info VARCHAR(MAX));
INSERT INTO #CompanyXml (company_xml_info ) VALUES ('<CompanyInfo>
<Businesses>
<Business id="A68">
<Businessceo>Test</Businessceo>
</Business>
</Businesses>
</CompanyInfo>')
,('<CompanyInfo>
<Businesses>
<Business id="A70">
<Businessceo>Test2</Businessceo>
</Business>
</Businesses>
</CompanyInfo>')
--Data as is
Select *
From #Data
--example of your code as is
SELECT *
From #CompanyXml
--exclusionary listing
SELECT *
From #Data
EXCEPT
--the secret of this is part 1 casting it to xml. Then you extend that with '.value'. That wants a structure to get to the Id.
--I wrap that in ()'s then say the first instance of that [1] as in theory you could have more instances and do very complex parsing.
--Then it needs a type of sql to transform this value into
SELECT CAST(company_xml_info AS XML).value('(CompanyInfo/Businesses/Business/#id)[1]', 'varchar(8)')
From #CompanyXml
Update 6-29-17
If you have something that has repeat elements in a tree structure of your XML, I prefer the 'nodes' method of repeating them and then you do not have to worry about using a first. You merely need to iterate through what you have from the use of the 'nodes' syntax and get a value like so
DECLARE #X XML = '<CompanyInfo><Businesses><Business id="C1405"/><Business id="C1408"/><Business id="C1408"/></Businesses> </CompanyInfo>'
SELECT
x.query('.')
, x.value('#id', 'varchar(8)')
FROM #X.nodes('/CompanyInfo/Businesses/Business') AS y(x)

Execute table valued function from row values

Given a table as below where fn contains the name of an existing table valued functions and param contains the param to be passed to the function
fn | param
----------------
'fn_one' | 1001
'fn_two' | 1001
'fn_one' | 1002
'fn_two' | 1002
Is there a way to get a resulting table like this by using set-based operations?
The resulting table would contain 0-* lines for each line from the first table.
param | resultval
---------------------------
1001 | 'fn_one_result_a'
1001 | 'fn_one_result_b'
1001 | 'fn_two_result_one'
1002 | 'fn_two_result_one'
I thought I could do something like (pseudo)
select t1.param, t2.resultval
from table1 t1
cross join exec sp_executesql('select * from '+t1.fn+'('+t1.param+')') t2
but that gives a syntax error at exec sp_executesql.
Currently we're using cursors to loop through the first table and insert into a second table with exec sp_executesql. While this does the job correctly, it is also the heaviest part of a frequently used stored procedure and I'm trying to optimize it. Changes to the data model would probably imply changes to most of the core of the application and that would cost more then just throwing hardware at sql server.
I believe that this should do what you need, using dynamic SQL to generate a single statement that can give you your results and then using that with EXEC to put them into your table. The FOR XML trick is a common one for concatenating VARCHAR values together from multiple rows. It has to be written with the AS [text()] for it to work.
--=========================================================
-- Set up
--=========================================================
CREATE TABLE dbo.TestTableFunctions (function_name VARCHAR(50) NOT NULL, parameter VARCHAR(20) NOT NULL)
INSERT INTO dbo.TestTableFunctions (function_name, parameter)
VALUES ('fn_one', '1001'), ('fn_two', '1001'), ('fn_one', '1002'), ('fn_two', '1002')
CREATE TABLE dbo.TestTableFunctionsResults (function_name VARCHAR(50) NOT NULL, parameter VARCHAR(20) NOT NULL, result VARCHAR(200) NOT NULL)
GO
CREATE FUNCTION dbo.fn_one
(
#parameter VARCHAR(20)
)
RETURNS TABLE
AS
RETURN
SELECT 'fn_one_' + #parameter AS result
GO
CREATE FUNCTION dbo.fn_two
(
#parameter VARCHAR(20)
)
RETURNS TABLE
AS
RETURN
SELECT 'fn_two_' + #parameter AS result
GO
--=========================================================
-- The important stuff
--=========================================================
DECLARE #sql VARCHAR(MAX)
SELECT #sql =
(
SELECT 'SELECT ''' + T1.function_name + ''', ''' + T1.parameter + ''', F.result FROM ' + T1.function_name + '(' + T1.parameter + ') F UNION ALL ' AS [text()]
FROM
TestTableFunctions T1
FOR XML PATH ('')
)
SELECT #sql = SUBSTRING(#sql, 1, LEN(#sql) - 10)
INSERT INTO dbo.TestTableFunctionsResults
EXEC(#sql)
SELECT * FROM dbo.TestTableFunctionsResults
--=========================================================
-- Clean up
--=========================================================
DROP TABLE dbo.TestTableFunctions
DROP TABLE dbo.TestTableFunctionsResults
DROP FUNCTION dbo.fn_one
DROP FUNCTION dbo.fn_two
GO
The first SELECT statement (ignoring the setup) builds a string which has the syntax to run all of the functions in your table, returning the results all UNIONed together. That makes it possible to run the string with EXEC, which means that you can then INSERT those results into your table.
A couple of quick notes though... First, the functions must all return identical result set structures - the same number of columns with the same data types (technically, they might be able to be different data types if SQL Server can always do implicit conversions on them, but it's really not worth the risk). Second, if someone were able to update your functions table they could use SQL injection to wreak havoc on your system. You'll need that to be tightly controlled and I wouldn't let users just enter in function names, etc.
You cannot access objects by referencing their names in a SQL statement. One method would be to use a case statement:
select t1.*,
(case when fn = 'fn_one' then dbo.fn_one(t1.param)
when fn = 'fn_two' then dbo.fn_two(t1.param)
end) as resultval
from table1 t1 ;
Interestingly, you could encapsulate the case as another function, and then do:
select t1.*, dbo.fn_generic(t1.fn, t1.param) as resultval
from table1 t1 ;
However, in SQL Server, you cannot use dynamic SQL in a user-defined function (defined in T-SQL), so you would still need to use case or similar logic.
Either of these methods is likely to be much faster than a cursor, because they do not require issuing multiple queries.

SQL XML Parsing Query for Element Hierarchy

I am attempting to write a SQL Query that will take in an XML object of undefined schema (YAY!) and transform it to a two column table of ElementName, Value columns. I was able to get a simple query down after some time (I am not a SQL person by any means).
DECLARE #strXml XML
SET #strXml = '<xml>
<FirstName>TEST</FirstName>
<LastName>PERSON</LastName>
<DOB>1/1/2000</DOB>
<TestObject>
<SomeProperty>CHECKED</SomeProperty>
<EmbeddedObject>
<SomeOtherProperty>NOT CHECKED</SomeOtherProperty>
</EmbeddedObject>
</TestObject>
</xml>'
DECLARE #XmlMappings TABLE
(
NodeName VARCHAR(64),
Value VARCHAR(128)
)
INSERT INTO #XmlMappings
SELECT doc.col.value('fn:local-name(.)[1]', 'varchar(64)') AS ElementName,
doc.col.value('.', 'varchar(128)') AS Value
FROM #strXml.nodes('/xml/*') doc(Col)
SELECT * FROM #XmlMappings
This query can handle the simple condition of the specified XML with only the first level elements. However elements such as TestObject and EmbeddedObject end up flattened. What I am looking for is to get some type of mapping like
ElementName | Value
=====================================================
FirstName | TEST
LastName | PERSON
DOB | 1/1/2000
TestObject.SomeProperty | CHECKED
TestObject.EmbeddedObject.SomeOtherProperty | NOT CHECKED
The hard part for me is the hierarchical structure with the . operator. I don't care if it is some other delimiter than . that gets output, it is more of just getting the output done, and I don't know enough about XML in SQL to be able to know even what to query.
Please note that I can also not use OPENXML since this is looking to be deployed on SQL Azure which does not support that feature at this time.
With a CTE and cross apply
;with cte as
(
select
convert(varchar(100), x.n.value('fn:local-name(.)','varchar(100)') ) as path,
convert(varchar(100), x.n.value('fn:local-name(.)','varchar(100)') ) AS name,
x.n.query('*') AS children,
x.n.value('.','varchar(1000)') as value
from #strxml.nodes('/xml/*') AS x(n)
union all
select
convert(varchar(100), x.path + '.' + c.n.value('fn:local-name(.)','varchar(100)') ),
convert(varchar(100), c.n.value('fn:local-name(.)','varchar(100)') ) ,
c.n.query('*'),
c.n.value('.','varchar(1000)')
from cte x
cross apply x.children.nodes('*') AS c(n)
)
select path, value from cte where datalength(children) = 5

SQL- Collect all data into a variable

i need to collect all return data into a variable using comma separated.
let say i have a select command like: select * from #temptable.
it's return:
Field1|Field2
-------------
Value1|Value2
Expected Result: #testvariable hold the value: 'Value1','Value2'
On this table their may have 2 columns and i need to store all the return result into a single variable. We can easily collect a single value like: select #var=column1 from #temptable. But i need to store all.Here the problem is, the number of column can be vary. Mean, number of column and name of column generate from another query.So, i can't mention the field name.I need a dynamic way to do it. on this table only one row will be return. Thanks in advance.
You can do this without dynamic SQL using XML
DECLARE #xml XML = (SELECT * FROM #temptable FOR XML PATH(''))
SELECT stuff((SELECT ',' + node.value('.', 'varchar(100)')
FROM #xml.nodes('/*') AS T(node)
FOR XML PATH(''), type).value('.','varchar(max)')
, 1, 1, '');
This can probably be simplified by someone more adept at XML querying than me.
Since your column names are dynamic, so first you have to take the column names as comma separated in a variable and then can use EXEC()
for example :-
//making comma seperated column names from table B
DECLARE #var varchar(1000)=SELECT SUBSTRING(
(SELECT ',' + Colnames
FROM TABLEB
ORDER BY Colnames
FOR XML PATH('')),2,200000)
//Execute the sql statement
EXEC('select '+#var+' from tableA')
if you want to get the value returned after execution of sql statement then you can use
sp_executesql (Transact-SQL)