How to join dynamic sql statement in variable with normal statement - sql

I have a quite complicated query which will by built up dynamically and is saved in a variable.
As second part i have another normal query and i'd like to make an inner join between these both.
To make it a little more easier here is a little example to illustrate my problem.
For this little example i used the AdventureWorks database.
Some query built up dynamically
(Yes, i know there is nothing dynamic here, cause it's just an example.)
DECLARE #query AS varchar(max) ;
set #query = '
select
HumanResources.Employee.EmployeeID
,HumanResources.Employee.LoginID
,HumanResources.Employee.Title
,HumanResources.EmployeeAddress.AddressID
from
HumanResources.Employee
inner join HumanResources.EmployeeAddress
on HumanResources.Employee.EmployeeID = HumanResources.EmployeeAddress.EmployeeID
;';
EXEC (#query);
The normal query i have
select
Person.Address.AddressID
,Person.Address.City
from
Person.Address
Maybe what i'd like to have but doesn't work
select
#query.*
,Addresses.City
from
#query as Employees
inner join
(
select
Person.Address.AddressID
,Person.Address.City
from
Person.Address
) as Addresses
on Employees.AddressID = Addresses.AddressID

Use temp tables & have the records dumped into it (from the dynamic query) & use the temp table to join with the static query that you have.
set #query = 'CREATE table #myTempTable AS
select
HumanResources.Employee.EmployeeID
,HumanResources.Employee.LoginID
,HumanResources.Employee.Title
,HumanResources.EmployeeAddress.AddressID
from
HumanResources.Employee
inner join HumanResources.EmployeeAddress
on HumanResources.Employee.EmployeeID = HumanResources.EmployeeAddress.EmployeeID
;';
EXEC (#query);
And then
select
Employees.*
,Addresses.City
from
#myTempTable as Employees
inner join
(
select
Person.Address.AddressID
,Person.Address.City
from
Person.Address
) as Addresses
on Employees.AddressID = Addresses.AddressID

You might be on the right track, but as long as you trust the source of the parameter and not exposed for SQL-Injection, you might just have to alter your select when building your #query like:
parameter to your function '#YourAlternateTableParm'
DECLARE #query AS varchar(max) ;
set #query = 'select ' + #YourAlternateTableParm
+ '.*, Addresses.City
from ' + #YourAlternateTableParm
+ ' as Employees
inner join
( ..... '
This way, like you were building your original string, you are building the actual value of the parameter to a function/procedure call with the table name desired to represent your "Employees" file into the string, then you execute that. SQL is not dynamically interpretting the #query inline the way you were trying to do.

Related

Retrieve info from a query where table name and value are parameters SQL

I am trying to retrieve a bunch of info from SQL procedure. It should receive two parameters, one of the parameters is a table name and the other is the value properly. Is there any way to retrieve that?
This is the query I've created so far:
CREATE PROCEDURE SP_Positions_Filtered
#Parameter VARCHAR(200),
#Id VARCHAR(50)
AS
SELECT
POSITION.*,
COALESCE((SELECT Category.Name
FROM Category
WHERE Category.Id = POSITION.Area), POSITION.Area) AS AreaName,
COALESCE((SELECT Country.Name
FROM Country
WHERE Country.Id = POSITION.Country), POSITION.Country) AS CountryName,
COALESCE((SELECT Department.Name
FROM Department
WHERE Department.Id = POSITION.Department), POSITION.Department) AS DepartmentName,
COALESCE((SELECT City.Name
FROM City
WHERE City.Id = POSITION.City), POSITION.City) AS CityName,
COALESCE((SELECT Salary.Data_Range
FROM Salary
WHERE Salary.Id = POSITION.Wage), POSITION.Wage) AS SalaryRange
FROM
POSITION
WHERE
(SELECT DISTINCT name FROM sys.columns WHERE name = #Parameter) = #Id
Could somebody help me out with this issue?
Thanks in advance.
You can achieve this using the Dynamic query inside the store procedure.
see example below:
CREATE Procedure FC_Sample
#TableName Varchar (100)
AS
BEGIN
DECLARE #query NVARCHAR(MAX);
set #query = 'SELECT * from' + ' ' + #TableName
EXECUTE sp_executesql #query
END
And you can execute the Stored procedure as below:
EXEC FC_Sample 'ACIChemical'
Hope this is what you are looking for.
NOTE: I am giving it in the Answer section as i don't have 50 reputation to comment on this question else i would have asked more question before posting the answer.

Expand all view-queries in query

I have queries like this:
SELECT
*
FROM vwUsers
LEFT JOIN tblLocations
ON vwUsers.id = tblLocations.owner
Is there an automated way to expand out this query to include the view statement (and any nested view statements)? I'd like to end up with something like:
SELECT * FROM
(SELECT id,name, -- vwUsers expanded into it's defining statement
FROM tblUsers
LEFT JOIN tblNames
ON tblUsers.id = tblNames.id) AS vwUsers
LEFT JOIN tblLocations
ON vwUsers.id = tblLocations.owner
The solution seems clear:
Create a list with all refereed views
Get their definitions
Replace the definitions in your query
Something like this in my environment:
DECLARE #Objects TABLE
(
[ObjectName] SYSNAME
,[Definition] NVARCHAR(MAX)
);
INSERT INTO #Objects ([ObjectName])
VALUES ('[dbo].[SurveyInstancesHistory]')
,('dbo.vw_MystClients');
UPDATE #Objects
SET [Definition] = M.[definition]
FROM #Objects O
INNER JOIN [sys].[objects] OB
ON OBJECT_ID([ObjectName]) = OB.[object_id]
INNER JOIN [sys].[sql_modules] M
ON OBJECT_ID([ObjectName]) = M.[object_id]
WHERE OB.[type] = 'V';
DECLARE #DynamicTSQLStatement NVARCHAR(MAX) = N'
SELECT *
FROM SurveyInstances SI
INNER JOIN [dbo].[SurveyInstancesHistory] SIH
ON SI.[SurveyInstanceID] = SIH.[SurveyInstanceID]
INNER JOIN ProtoSurveys PS
ON SI.[ProtoSurveyID] = PS.[ProtoSurveyID]
INNER JOIN dbo.vw_MystClients MC
ON PS.[ClientID] = MC.[Client<br>ID];'
SELECT #DynamicTSQLStatement = REPLACE(#DynamicTSQLStatement, [ObjectName], '(' + [Definition] + ') AS ' + [ObjectName])
FROM #Objects;
SELECT #DynamicTSQLStatement;
In order this to be automated the following cases must be handled:
we can automatically get the referred objects using regex matches
we can automatically replace the CREATE VIEW .... AS statement using regex
In you need a script that's always working in your example you need to write the regex expressions by yourself as people are creating views with different syntax. As the above can be used and edited by hand in order to run, a very complex script can be created depending on your needs.
Strongly recommenced to implement the String Utility Functions Sample in order to get regex support in T-SQL.

Ho to write Select query for EAV - Entity Attribute Value model

I have following schema. In which
Types represent DB Table
TypeProperty represents Column of Table
TypeRow represent Row of table
I want to write a select query to which I will pass single Type and it should give me all its TypeProperty, TypeRow and TypeValue that are associated with these Properties and Rows.
I will be showing this data in a web application in which user will select a Type from dropdown and application will get Properties, Rows and associated values and will show them as a complete grid.
I am using SQL Server 2014.
Can anyone help me please?
So, i'm going to try and take a crack at what you have been getting help with Kannan on.
It sounds like you have two different queries to the database:
1) Query results of the list of 'Type(s)' for your dropdown,(You should be able to do this fairly easily)
2) query results of the list of 'Propert(ies)', 'Row(s)', and 'value(s) that match the selected 'Type' in the dropdown as a table with the properties as the header to set table.
To me it seems the easest and best way to handle this would be to get the data back using Kannan's script (probably inside of a stored procedure and maybe a view?) and create the grid in code from your back-end application, or front end client. However, if you cant, here is a script that should work or at the least get you started.
I would suggest maybe creating two stored procs, one to retrieve the data, and another to pivot using dynamic sql.
CREATE PROCEDURE dbo.EAV_GridGenerator
#TypeId int = 0,
#param2 int
AS
BEGIN TRY
DECLARE #cols varchar(max),
#query varchar(max);
--TODO: CLEAN UP VARIABLE NAMES THROUGHOUT
SELECT trow.TypesId, tprop.PropertyName AS [Column], trow.TypeRowId AS [RowID], tval.Value AS [Data]
INTO #TT2
FROM dbo.[Types] AS t
JOIN dbo.TypeRow trow
ON t.typesId = trow.typesId
JOIN dbo.TypeValue tval
ON tval.TypeRowsId = trow.TypeRowId
JOIN dbo.[TypeProperty] tprop
ON tval.TypesPropertyId = tprop.TypePropertyId
WHERE trow.TypesId = #TypeId
--AND t.IsActive = 1 AND tprop.IsActive = 1 AND trow.IsActive = 1 AND tval.IsActive = 1--TODO: IDK but you should probably add both of these
-- AND t.IsDelete = 1 AND tprop.IsDelete = 1 AND trow.IsDelete = 1 AND tval.IsDelete = 1--TODO: IDK but you should probably add both of these
ORDER BY RowID, [Column], Data
SELECT #cols = STUFF(( SELECT DISTINCT TOP 100 PERCENT
'],[' + t.[Column]
FROM #TT2 AS t
--ORDER BY '],[' + t.ID
FOR XML PATH('')
), 1, 2, '') + ']'
SET #query = N'SELECT RowID,'+ #cols +' FROM
(SELECT tt2.RowID,tt2.[Column] , tt2.Data FROM #tt2 AS tt2) p
PIVOT (max([data]) FOR [Column] IN ( '+ #cols +'))
AS pvt;'
EXECUTE(#query)
drop table #TT2
END TRY
BEGIN CATCH
--TODO: PROPER CATCH
END CATCH
A simple join will work.. Are you looking this?
Select * --your required columns
from Types t
inner join TypesProperty tp
on t.TypesId = tp.TypesId
inner join TypeRow tr
on t.TypesId = tr.TypesId
Left join TypeValue tv
on tp.TypesPropertyId = tv.TypesPrpertyId
--You need to join using typeRowid with typeValue if your require value details

Dynamic SP returning values in reverse order

I am using MS SQL and created one Dynamic stored procedure:
ALTER Procedure [dbo].[sp_MTracking]
(
#OList varchar(MAX)
)
As
BEGIN TRY
SET NOCOUNT ON
DECLARE #SQL varchar(600)
SET #SQL = 'select os.X,os.Y from Table1 as os join Table2 as s on os.sID=s.sID where s.SCode IN ('+ #OList +')'
exec (#SQL)
END TRY
BEGIN CATCH
Execute sp_DB_ErrorInfo
Select -1 Result
END CATCH
GO
It is working properly, but I am getting x,y values in reverse order.
For example if I am passing 'scode1,scode2' as parameter, I am getting x,y values for scode1 in 2nd row and x,y values for scode2 as first row.
How can I fix this issue
Thanks
This is a bit long for a comment.
SQL tables and results sets represent unordered sets. There is no ordering, unless you explicitly use an ORDER BY clause.
Your query does not have an ORDER BY. Hence, you have no reason to expect the results in any particular order. In addition, the ordering may be different on different runs of the query. If you want the results in a particular order, add ORDER BY.
Probably the easiest way is to use charindex():
order by charindex(',' + s.code + ',' , ',''' + #olist + ''',')
This is a bit more cumbersome in dynamic sql:
SET #SQL = '
select os.X,os.Y
from Table1 os join
Table2 s
on os.sID = s.sID
where s.SCode IN (' + #OList + ')
order by charindex('','' + s.code + '','', '',''' + #OList + ''', '')
';
Well, there are a couple of things here.
The first thing is what Gordon wrote - to ensure the order of the result set you must use the order by clause.
Second, like Devart demonstrated in his answer, you don't need dynamic sql for this kind of procedures.
Third, if you want your results ordered by the order of the parameters in the list, you should use a slightly different approach then Devart wrote.
Therefor, here are my 2 cents:
If you can change the stored procedure to accept a table valued parameter instead of VARCHAR(max) that would be your best option IMHO.
If not, you must use a split function to create a table from that varchar and then use that table in your select.
Note that you will have to choose a split function that returns a table with two columns - one for the value and one for it's position in the original string.
Whatever the case may be, the rest of the sql should be something like this:
SELECT os.X, os.Y
FROM Table1 os
INNER JOIN Table2 s ON os.[sID] = s.[sID]
INNER JOIN #TVP t ON s.SCode = t.Value
ORDER BY t.Sort
That's assuming #TVP to be a Table containing a Value column that's the same data type of SCode in table2, and a Sort column (an int, naturally).
Without dynamic sql -
ALTER PROCEDURE [dbo].[sp_MTracking]
(
#OList VARCHAR(MAX)
)
AS BEGIN
SET NOCOUNT ON
DECLARE #t TABLE (val VARCHAR(50) PRIMARY KEY WITH(IGNORE_DUP_KEY=ON))
INSERT INTO #t
SELECT item = t.c.value('.', 'INT')
FROM (
SELECT txml = CAST('<r>' + REPLACE(#OList, ',', '</r><r>') + '</r>' AS XML)
) r
CROSS APPLY txml.nodes('/r') t(c)
SELECT os.X, os.Y
FROM Table1 os
JOIN Table2 s ON os.[sID] = s.[sID]
WHERE s.SCode IN (SELECT * FROM #t)
--OPTION(RECOMPILE)
END
GO

Columns not showinng in DataSet for dynamic SQL

I have the following SP which works correctly when ran on its own:
ALTER PROCEDURE [dbo].[sgetInvoiceHeaderDetails]
#InvoiceNo varchar(max)
AS
BEGIN
SET FMTONLY ON;
declare #sql varchar(max)
set #sql = 'SELECT IH.InvoiceNo, IH.InvoiceDate, IH.InvoiceTime, C.Name, R.Name AS Customer, IH.NetAmount,
IM.Name AS Item, ID.UnitPrice, ID.Qty, ID.Total, ID.BatchNo
FROM InvoiceHeader AS IH INNER JOIN
InvoiceDetail AS ID ON IH.InvoiceNo = ID.InvoiceNo INNER JOIN
Customer AS C ON IH.CustomerId = C.Id INNER JOIN
Route AS R ON IH.RouteId = R.Id INNER JOIN
ItemMaster AS IM ON ID.ItemMasterId = IM.Id
WHERE IH.InvoiceNo IN ('+#InvoiceNo+')'
print #sql
exec (#sql)
END
The problem I'm having is that when I add a DataSet for a report, it pulls no fields/columns in the Fields section. I'm guessing it's due to the dynamic SQL?
How can I resolve that?
As statet in my comment you should avoid the dynamic approach.
Just to offer you a pure inline solution in SQL have a look at this:
DECLARE #tbl TABLE(ID INT, Caption VARCHAR(100));
INSERT INTO #tbl VALUES(1,'Test 1'),(2,'Test 2'),(3,'Test 3'),(4,'Test 4'),(5,'Test 5');
DECLARE #WantToGet VARCHAR(100)='1,3,4';
WITH Splitted AS
(
SELECT CAST('<x>' + REPLACE(#WantToGet,',','</x><x>') + '</x>' AS XML) AS AsXml
)
,SplittedAsList AS
(
SELECT The.Node.value('.','int') As ID
FROM Splitted
CROSS APPLY AsXml.nodes('/x') AS The(Node)
)
SELECT Caption
FROM #tbl AS tbl
INNER JOIN SplittedAsList sal ON sal.ID = tbl.ID;
The string 1,3,4 is splitted as a list. The INNER JOIN at the end is exactly the same as you wanted to achieve with the IN-clause.
This approach you can plcae within a table valued function (make sure to keep this as inline function!). This function is much better reusable everywhere.
The second recommandable approach would be the CREATE TYPE, bute this needs more action on application side...