Conditional statement to determine inner join or left outer join? - sql

I have a large millions+ rows of record that in a table that I want to do an inner join when a parameter is provided, if not do a filter based search. Long term solution is to split up the logic, but I need some assistance in the short term.
I'm trying to do something like below:
params:
#movie_ids int_table (optional passing in a table of only ints here)
#filter_name
#filter_genre
#filter_actor_name
DECLARE #id_count = sets movie id count here or 0;
SELECT
m.Id
m.Name
m.ShortName
m.Genre
m.ImageUrl
FROM Movies m WITH(NOLOCK)
IF movie_ids <> 0
BEGIN
INNER JOIN #movie_ids mids
ON mids.id = m.Id
END
ELSE
LEFT OUTER JOIN code_genres cg
ON m.GenreId = cg.Id
INNER JOIN Actors a
ON a.name LIKE '%#filter_actor_name%'
END
WHERE m.Name LIKE '%filter_name%'
OR m.ShortName LIKE '%filter_name%'
Sorry I couldn't provide the actual stored procedure since I'm not the original writer. The problem in short term I'm trying to figure out is to find out if I can do this conditional joins.
Currently it has a LEFT OUTER JOIN with a where clause that suppose to do the conditional, but the amount of data it is returning before the filter occurs is too much - too slow. Something like here
Let me know if I can provide more details.
(Long terms solution is done - split up the logic from sql, but I need something for now)

As Programnik mentioned, you can go for dynamic sql here. You can try something like this:
DECLARE #sql VARCHAR(1000) = '';
#sql = #sql + 'SELECT
m.Id
m.Name
m.ShortName
m.Genre
m.ImageUrl
FROM Movies m WITH(NOLOCK)';
IF movie_ids <> 0
BEGIN
#sql = #sql + ' INNER JOIN #movie_ids mids
ON mids.id = m.Id';
END
ELSE
#sql = #sql + ' LEFT OUTER JOIN code_genres cg
ON m.GenreId = cg.Id
INNER JOIN Actors a
ON a.name LIKE' +'%#filter_actor_name%';
END
#sql = #sql + ' WHERE m.Name LIKE' + '%filter_name%';
#sql = #sql + ' OR m.ShortName LIKE' + '%filter_name%';
EXEC sp_executesql #sql

Related

Convert all rows into different in sql server

I have a stored procedure that is showing a list of doctors and their details based on the sub-department they belong to. Below is the stored proc:
CREATE PROCEDURE SP_BILL_FOOTER_DOCTOR
#subDepartmentId int
AS
BEGIN
SELECT HETC_MST_EMPLOYEE.EMPLOYEE_NAME,
HETC_PAR_EMPLOYEE_TYPE.EMPLOYEE_TYPE_NAME,
HETC_MST_DOCTOR_SPECIALITY.DOCTOR_SPECIALITY_DESCRIPTION,
HETC_MST_SUB_DEPARTMENT.SUB_DEPARTMENT_NAME,
HETC_MST_EMPLOYEE.DOCTOR_SIGNATURE,
CASE WHEN HETC_MST_EMPLOYEE.DOCTOR_SIGNATURE = ''
THEN ''
ELSE ISNULL(SIGNATURE_PATH.DOCUMENT_PATH,'')+ HETC_MST_EMPLOYEE.DOCTOR_SIGNATURE
END AS DOCTOR_SIGNATURE_PIC
FROM HETC_MST_EMPLOYEE
INNER JOIN HETC_PAR_EMPLOYEE_TYPE
ON HETC_PAR_EMPLOYEE_TYPE.EMPLOYEE_TYPE_ID = HETC_MST_EMPLOYEE.EMPLOYEE_TYPE_ID
INNER JOIN HETC_MST_DOCTOR_SPECIALITY
ON HETC_MST_DOCTOR_SPECIALITY.DOCTOR_SPECIALITY_ID = HETC_MST_EMPLOYEE.DOCTOR_SPECIALITY_ID
INNER JOIN HETC_MST_DOCTOR_DEPARTMENT
ON HETC_MST_DOCTOR_DEPARTMENT.EMPLOYEE_ID = HETC_MST_EMPLOYEE.EMPLOYEE_ID
INNER JOIN HETC_MST_SUB_DEPARTMENT
ON HETC_MST_SUB_DEPARTMENT.SUB_DEPARTMENT_ID = HETC_MST_DOCTOR_DEPARTMENT.SUB_DEPARTMENT_ID
LEFT JOIN (SELECT DOCUMENT_PATH
FROM HETC_MST_DOCUMENT_PATH
INNER JOIN HETC_MST_TYPE_OF_ATTACHMENT
ON HETC_MST_DOCUMENT_PATH.TYPE_OF_DOCUMENT_ID = HETC_MST_TYPE_OF_ATTACHMENT.TYPE_OF_DOCUMENT_ID
WHERE HETC_MST_TYPE_OF_ATTACHMENT.TYPE_OF_DOCUMENT_CODE='DSI') AS DOC_SIGNATURE_PIC
ON 1=1
WHERE HETC_MST_SUB_DEPARTMENT.SUB_DEPARTMENT_ID = #subDepartmentId
END
Below is the link of the output that follows when procedure executes :
I want to know is it possible to convert the rows in different column. Like the output has 6 columns and 2 rows, I want all the data in 1 row with 12 columns. Below is the sample output:
It would be of great help if somebody could guide me on how to do it. I have understood that by using Pivot in Sql, I can achieve this, but none I have found to my specific case.
Please have a look at updated code below:
select *, row_number() over(order by employee_name) rownum into #a from (
SELECT HETC_MST_EMPLOYEE.EMPLOYEE_NAME,
HETC_PAR_EMPLOYEE_TYPE.EMPLOYEE_TYPE_NAME,
HETC_MST_DOCTOR_SPECIALITY.DOCTOR_SPECIALITY_DESCRIPTION,
HETC_MST_SUB_DEPARTMENT.SUB_DEPARTMENT_NAME,
HETC_MST_EMPLOYEE.DOCTOR_SIGNATURE,
CASE WHEN HETC_MST_EMPLOYEE.DOCTOR_SIGNATURE = ''
THEN ''
ELSE ISNULL(SIGNATURE_PATH.DOCUMENT_PATH,'')+ HETC_MST_EMPLOYEE.DOCTOR_SIGNATURE
END AS DOCTOR_SIGNATURE_PIC
FROM HETC_MST_EMPLOYEE
INNER JOIN HETC_PAR_EMPLOYEE_TYPE
ON HETC_PAR_EMPLOYEE_TYPE.EMPLOYEE_TYPE_ID = HETC_MST_EMPLOYEE.EMPLOYEE_TYPE_ID
INNER JOIN HETC_MST_DOCTOR_SPECIALITY
ON HETC_MST_DOCTOR_SPECIALITY.DOCTOR_SPECIALITY_ID = HETC_MST_EMPLOYEE.DOCTOR_SPECIALITY_ID
INNER JOIN HETC_MST_DOCTOR_DEPARTMENT
ON HETC_MST_DOCTOR_DEPARTMENT.EMPLOYEE_ID = HETC_MST_EMPLOYEE.EMPLOYEE_ID
INNER JOIN HETC_MST_SUB_DEPARTMENT
ON HETC_MST_SUB_DEPARTMENT.SUB_DEPARTMENT_ID = HETC_MST_DOCTOR_DEPARTMENT.SUB_DEPARTMENT_ID
LEFT JOIN (SELECT DOCUMENT_PATH
FROM HETC_MST_DOCUMENT_PATH
INNER JOIN HETC_MST_TYPE_OF_ATTACHMENT
ON HETC_MST_DOCUMENT_PATH.TYPE_OF_DOCUMENT_ID = HETC_MST_TYPE_OF_ATTACHMENT.TYPE_OF_DOCUMENT_ID
WHERE HETC_MST_TYPE_OF_ATTACHMENT.TYPE_OF_DOCUMENT_CODE='DSI') AS DOC_SIGNATURE_PIC
ON 1=1
WHERE HETC_MST_SUB_DEPARTMENT.SUB_DEPARTMENT_ID = #subDepartmentId )a
declare #iterator int=1
declare #string varchar(max)= ''
declare #string2 varchar(max)= ''
declare #string3 varchar(max)= ''
declare #string4 varchar(max)= ''
declare #exec varchar(max)
while #iterator<=(select max(rownum) from #a)
begin
select #string2=
'['+cast(#iterator as varchar(max))+'].'+ 'EMPLOYEE_NAME'+
',['+cast(#iterator as varchar(max))+'].'+'EMPLOYEE_TYPE_NAME' +
',['+cast(#iterator as varchar(max))+'].'+'DOCTOR_SPECIALITY_DESCRIPTION' +
',['+cast(#iterator as varchar(max))+'].'+'SUB_DEPARTMENT_NAME' +
',['+cast(#iterator as varchar(max))+'].'+'DOCTOR_SIGNATURE'+
',['+cast(#iterator as varchar(max))+'].'+'DOCTOR_SIGNATURE_PIC'
from #a where rownum=#iterator
select #string= #string+#string2
select #string4=
case when #string4='' then
#string4+'['+cast(#iterator as varchar(max))+'].rownum='+cast(#iterator as varchar(max)) else
#string4+' and ['+cast(#iterator as varchar(max))+'].rownum='+cast(#iterator as varchar(max)) end
select #string3= case when #iterator>1 then #string3+' cross join #a ['+ cast(#iterator as varchar(max))+']' else '' end
set #iterator=#iterator+1
end
select #exec = 'select distinct'+ left(#string, len(#string)-1) +' from #a [1] '+#string3+ ' where '+ #string4
exec(''+#exec+'')
This isn't really an answer but a demonstration of how much using aliases can improve the legibility of your queries. Believe it or not this EXACTLY the same thing you posted. I just used aliases so you can read this instead of looking at a wall of text. The only actual change was to use a cross join instead of a left join on 1 = 1.
SELECT e.EMPLOYEE_NAME,
et.EMPLOYEE_TYPE_NAME,
s.DOCTOR_SPECIALITY_DESCRIPTION,
sd.SUB_DEPARTMENT_NAME,
e.DOCTOR_SIGNATURE,
CASE WHEN e.DOCTOR_SIGNATURE = ''
THEN ''
ELSE ISNULL(SIGNATURE_PATH.DOCUMENT_PATH, '') + e.DOCTOR_SIGNATURE
END AS DOCTOR_SIGNATURE_PIC
FROM HETC_MST_EMPLOYEE e
INNER JOIN HETC_PAR_EMPLOYEE_TYPE et ON et.EMPLOYEE_TYPE_ID = e.EMPLOYEE_TYPE_ID
INNER JOIN HETC_MST_DOCTOR_SPECIALITY s ON s.DOCTOR_SPECIALITY_ID = e.DOCTOR_SPECIALITY_ID
INNER JOIN HETC_MST_DOCTOR_DEPARTMENT dd ON dd.EMPLOYEE_ID = e.EMPLOYEE_ID
INNER JOIN HETC_MST_SUB_DEPARTMENT sd ON sd.SUB_DEPARTMENT_ID = dd.SUB_DEPARTMENT_ID
cross join
(
SELECT DOCUMENT_PATH
FROM HETC_MST_DOCUMENT_PATH p
INNER JOIN HETC_MST_TYPE_OF_ATTACHMENT a ON p.TYPE_OF_DOCUMENT_ID = a.TYPE_OF_DOCUMENT_ID
WHERE a.TYPE_OF_DOCUMENT_CODE='DSI'
) AS DOC_SIGNATURE_PIC
WHERE sd.SUB_DEPARTMENT_ID = #subDepartmentId
For the question at hand it is hard to tell what you are really wanting here. Maybe some conditional aggregation in combination with ROW_NUMBER. Or a PIVOT. You would need to post more details for this. Here is a great place to start. http://spaghettidba.com/2015/04/24/how-to-post-a-t-sql-question-on-a-public-forum/

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.

Using CSV string Values in 'IN' clause of Dynamic SQL

I have a stored procedure which accepts csv string as the parameter , for eg. ('IN\libin.jose,IN\Pallabi.P'). Stored procedure contains some dynamic sql which make use of this parameter inside IN clause eg : (AND u1.UserName IN (' + #UserNames + ')). Since this is being inside dynamic sql ,The condition is not passing without appending extra single quotes to the CSV values eg ('''IN\libin.jose '' ,''IN\Pallabi.P''')
--exec [GetUwParameterDetails] 'IN\libin.jose,IN\Pallabi.P' , 'false'
ALTER PROCEDURE [dbo].[GetUwParameterDetails]
#UserNames nvarchar(max),
#IncludeInactiveusers bit
AS
BEGIN
declare #selectedUsers nvarchar(max)
--set #selectedUsers = '''IN\libin.jose '' ,''IN\Pallabi.P''';
set #selectedUsers = 'IN\libin.jose,IN\Pallabi.P';
declare #selectedPermissions nvarchar(max)
set #selectedPermissions = '''Underwrite'',''ManageUwTeamPipeline''';
DECLARE #parameterQuery1 AS NVARCHAR(MAX);
set #parameterQuery1 = '
;WITH cte_users
AS (
SELECT users.id
,users.UserName
,users.FirstName
,users.lastname
,users.Email
,users.E3UserName
,UserStatus.[Status]
,Widgets.[Description] DefaultWidget
FROM users
INNER JOIN userparametervalues upv ON users.id = upv.userid
INNER JOIN Parameters p on upv.ParameterId = p.id
AND p.Name = ''UwHierarchy''
INNER JOIN UserPermissions up ON users.id = up.userid
INNER JOIN [Permissions] ps on up.PermissionId = ps.Id
AND ps.IsActive = 1 AND ps.Name IN ('+ #selectedPermissions +')
INNER JOIN users AS u1 ON upv.value = u1.id
AND u1.UserName IN (' + #UserNames + ')
INNER JOIN UserStatus ON users.StatusId = UserStatus.Id
LEFT JOIN Widgets ON users.WidgetId = Widgets.Id )select * from cte_users ';
exec #parameterQuery1
END
How can I achieve this ?
Use this after BEGIN
set #UserNames =''''+replace(#usernames,',',''',''')+''''

How to join dynamic sql statement in variable with normal statement

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.

Join with dynamic pivot

Followed up by a new question, that contains the question as text, not as an image like this one:
Join with dynamic pivot (version 2)
(This question is an image. Right click on "I have some table with value". :)
alt text http://img36.imageshack.us/img36/4853/77517349.gif
The query should automaticly work if a new position record is added. Thanks you
SELECT
c.CategoryId AS CID,
c.CategoryName,
ISNULL(t.CategoryOrder, 0) AS [Top],
ISNULL(l.CategoryOrder, 0) AS [Left],
ISNULL(r.CategoryOrder, 0) AS [Right]
FROM
Category c
LEFT JOIN CategoryPosition t ON t.CategoryId = c.CategoryId
AND t.PositionId = 1
LEFT JOIN CategoryPosition l ON l.CategoryId = c.CategoryId
AND l.PositionId = 2
LEFT JOIN CategoryPosition r ON r.CategoryId = c.CategoryId
AND r.PositionId = 3
Messy, but it works
select c.categoryid,c.categoryname
,COALESCE((select top 1 categoryorder from categoryposition where categoryid=c.categoryid and positionid=1),0) as [top]
,COALESCE((select top 1 categoryorder from categoryposition where categoryid=c.categoryid and positionid=2),0) as [left]
,COALESCE((select top 1 categoryorder from categoryposition where categoryid=c.categoryid and positionid=3),0) as [right]
from categoryposition cp,category c
where cp.categoryid=c.categoryid
group by c.categoryid,c.categoryname
order by 1
Two things to keep in mind. If you can ensure that there is at most one position for each categoryposition then you can remove the top 1, but the subquery must return 1 row o nothing for it to work.
Since you want the cross tab query to be dynamic based on the contents of Position table then I recommend that you dynamicly generate the SQL at runtime.
-- Start with Query frame
DECLARE #Query NVARCHAR(4000)
SET #Query = '
Select
Category.CategoryID,
Category.CategoryName
<DYNAMICQUERY>
From CategoryPosition
Inner Join Category ON Category.CategoryID = CategoryPosition.CategoryID
Group By Category.CategoryID, Category.CategoryName';
SELECT #Query;
-- Build the dynamic part of query
DECLARE #DynamicQuery VARCHAR(1024)
DECLARE #PositionCol VARCHAR(256)
DECLARE dynamic_sql CURSOR FOR
SELECT ',MAX(CASE WHEN CategoryPosition.PositionID = '+CAST(PositionID AS varchar(10)) +' THEN CategoryPosition.CategoryOrder ELSE 0 END) AS ['+PositionName+']'
From Position
OPEN dynamic_sql
FETCH NEXT FROM dynamic_sql
INTO #PositionCol
SELECT #DynamicQuery = #PositionCol;
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM dynamic_sql
INTO #PositionCol
SELECT #DynamicQuery = #DynamicQuery+#PositionCol;
END
CLOSE dynamic_sql
DEALLOCATE dynamic_sql
SELECT #Query = REPLACE(#Query, '<DYNAMICQUERY>', #DynamicQuery)
-- Execute the Query
EXECUTE sp_executesql #Query