Why is it not possible to set a column's alias dynamically? - sql

I have a stored procedure and I want to pass a column alias as a parameter, something like:
SELECT u.userLoginName AS #columnName
FROM -- some JOINs
WHERE -- some conditions
where #columnName can be one of two options and it is set before the SELECT statement according to some condition.
I already know that it can be done only by dynamic SQL, but I don't understand why?
I know that the Order of execution of a Query is: FROM and JOINs -> WHERE -> GROUP BY and only then SELECT.
So if at this point I already got the result set, i.e, the finale table, why can't I just rename the column name as #columnName? What happens in the background that I miss?

This may answer your question.
A SQL result set is conceptually just like a table: it has well defined rows and columns and no ordering unless created with an explicit order by.
A SQL query is processed in two phases: it is compiled and optimized, then it is run. (Happily some databases are now starting to provide dynamic optimization as well, but the queries still go through the compilation phase.)
All information about the result set needs to be known during the compilation phase -- and that includes the resulting column names and column types. Dynamic names would prevent this from happening. They would only be known during the execution phase.
Note that this applies to parameters as identifier as well. Parameters are substituted at the beginning of the execution phase.
This is not a limitation of any particular database. It applies to all of them. I suspect that some more modern databases are implemented in a way that would allow for more dynamic naming, but I don't know of any databases that actually implement it except through dynamic SQL.

It is possible but you have to use a dynamic query.
Let assume we have the following table
Create table #TBL ([Months] VARCHAR(3), Value INT)
INSERT INTO #TBL values
('Jan',20),('Feb',12),('Jan',15),('Mar',25),
('Feb',18),('Jan',9),('Mar',10),('Jan',19)
GO
And I want to dynamically set the columns name using variable. I can use the bellow code
DECLARE #M VARCHAR(10)='Months',
#T VARCHAR(10)='Total'
-- Dynamic query to get the column name
DECLARE #qry VARCHAR(MAX)
SET #qry = 'SELECT [Months] AS '+#M+',
sum(Value) as '+#T+'
FROM #TBL
group by [Months]
DROP TABLE #TBL'
EXEC (#qry)
Note the query itself have to be dynamic

Related

INSERT FROM EXISTING SELECT without amending

With GDPR in the UK on the looming horizon and already have a team of 15 users creating spurious SELECT statements (in excess of 2,000) across 15 differing databases I need to be able to create a method to capture an already created SELECT statement and be able to assign surrogate keys/data WITHOUT rewriting every procedure we already have.
There will be a need to run the original team members script as normal and there will be requirements to pseudo the values.
My current thinking is to create a stored procedure along the lines of:
CREATE PROC Pseudo (#query NVARCHAR(MAX))
INSERT INTO #TEMP FROM #query
Do something with the data via a mapping table of real and surrogate/pseudo data.
UPDATE #TEMP
SET FNAME = (SELECT Pseudo_FNAME FROM PseudoTable PT WHERE #TEMP.FNAME = PT.FNAME)
SELECT * FROM #TEMP
So that team members can run their normal SELECT statements and get pseudo data simply by using:
EXEC Pseudo (SELECT FNAME FROM CUSTOMERS)
The problem I'm having is you can't use:
INSERT INTO #TEMP FROM #query
So I tried via CTE:
WITH TEMP AS (#query)
..but I can't use that either.
Surely there's a way of capturing the recordset from an existing select that I can pull into a table to amend it or capture the SELECT statement; without having to amend the original script. Please bear in mind that each SELECT statement will be unique so I can't write COLUMN or VALUES etc.
Does any anyone have any ideas or a working example(s) on how to best tackle this?
There are other lengthy methods I could externally do to carry this out but I'm trying to resolve this within SQL if possible.
So after a bit of deliberation I resolved it.
I passed the Original SELECT SQL to SP that used some SQL Injection, which when executed INSERTed data. I then Updated from that dataset.
The end result was "EXEC Pseudo(' Orginal SQL ;')
I will have to set some basic rules around certain columns for now as a short term fix..but at least users can create NonPseudo and Pseudo data as required without masses of reworking :)

Find out all useful columns in a table in sql server

I have a table which has 50+ columns but only few columns are getting used. that means when any stored procedure uses that table it only refers 4-5 columns in select/where statements . rest of columns are not getting used . i just want to list down those columns that are actually getting used. one way is finding out the dependencies of a table and then go through every SP and find out which columns are getting used . but in that case i have around 30+ Sp. is there any efficient way to do it.
To use multiple columns in a procedure, you can use a code like below
create procedure sp_sample
#column_names varchar(200)
as
if #column_names='' or #column_nams is null
set #column_names='*'
exec ('select '+#column_name +' from table')
Here are some examples :
exec sp_sample #columnname='id,name'
or
exec sp_sample #columnname='id,name,telphone'
Try this:
select name from syscomments c
join sysobjects o on c.id = o.id
where TEXT like '%table_name%' and TEXT like '%column_name%'
In table_name give you table name, in column_name give the column for which you want to chck the procedure dependencies.You will get the stored procedure names as output
If you import your database as a database project using the SQL Server Data Tools, you will be able to find all references to a table or column using the "Find All References" context command. What makes this particularly useful is the accuracy: it will even find instances of SELECT * that don't mention the column explicitly, but implicitly refer to it anyway. It will also not be confused by tables or columns with similar names (finding particular instances of ID is otherwise rather problematic).
If all you want to know if a column is referenced at all, you can simply delete it and see if any "unresolved reference" errors appear in the error list -- if yes, then the column is used somewhere.

How do I perform selection operations on only existing columns in temporary table?

Why can't I perform selection operations after checking for column existence in a temporary table?
IF OBJECT_ID(''tempdb..#tempTable.Column'') IS NOT NULL
SELECT Column from #tempTable
--Error: Invalid column name 'Column'.
Is this error thrown because of the order of operations in SQL Server? If so, is there any way to delay the evaluation of the selection until the existence criteria is satisfied?
This is a problem that arises because the code is compiled first and then executed afterwards. The compiler checks that all columns and tables are available.
Now, the "normal" way to get around these problems is to use dynamic SQL. Something like:
IF OBJECT_ID(''tempdb..#tempTable.Column'') IS NOT NULL
exec sp_executesql N'SELECT Column from #tempTable';
But, this won't work in your case. The problem is that the #temptable is not understood in the context of the dynamic SQL.
Alas, I don't think you can do what you want with temporary tables. You could do it with a permanent table as:
IF OBJECT_ID('_tempTable.Column') IS NOT NULL
exec sp_executesql N'SELECT Column from _tempTable';
You can probably guess that when I have "temporary" tables in a non-temporary database, I use _ to distinguish them from other tables.
EDIT:
If you want to determine if a column is in a temporary table, then use logic such as this:
select *
from tempdb.sys.columns
where object_id = object_id('tempdb..#mytemptable');
(Courtesy of this answer -- which I note that I had already upvoted.)

Checking for a string within a string, and not in another string using SQL

I am trying to build a short SQL script that will check if #NewProductAdded is somewhere in #NewTotalProducts. And also if #NewProductAdded is NOT in #OldTotalProducts. Please have a look at the setup below (The real data is in tables, not variables, but a simple example is all I need):
declare #NewProductAdded as varchar(max)
declare #NewTotalProducts as varchar(max)
declare #OldTotalProducts as varchar(max)
set #NewProductAdded ='ProductB'
set #NewTotalProducts = 'ProductAProductBProductC'
set #OldTotalProducts = 'ProductAProductC'
SELECT CustomerID FROM Products WHERE NewProductAdded ...
I want to make sure that 'ProductB' is contained somewhere within #NewTotalProducts, and is NOT contained anywhere within #OldTotalProducts. Product names vary vastly with thousands of combinations, and there is no way to really separate them from each other in a string. I am sure there is a simple solution or function for this, I just don't know it yet.
The specific answer to your question is like (or charindex() if you are using SQL Server or Sybase):
where #NewTotalProducts like '%'+#NewProductAdded+'%' and
#OldTotalProducts not like '%'+#NewProductAdded+'%'
First comment. If you have to use lists stored in strings, at least use delimiters:
where ','+#NewTotalProducts+',' like '%,'+#NewProductAdded+',%' and
','+#OldTotalProducts+',' not like '%,'+#NewProductAdded+',%'
Second comment. Don't store lists in strings. Instead, use a temporary tables or table variable:
declare #NewTotalProducts table (name varchar(255));
insert into #NewTotalProducts(name)
select 'ProductA' union all
select 'ProductB' . . .
Note: throughout this answer I have used SQL Server syntax. The code appears to be SQL Server.

Optimizing stored procedure with multiple "LIKE"s

I am passing in a comma-delimited list of values that I need to compare to the database
Here is an example of the values I'm passing in:
#orgList = "1123, 223%, 54%"
To use the wildcard I think I have to do LIKE but the query runs a long time and only returns 14 rows (the results are correct, but it's just taking forever, probably because I'm using the join incorrectly)
Can I make it better?
This is what I do now:
declare #tempTable Table (SearchOrg nvarchar(max) )
insert into #tempTable
select * from dbo.udf_split(#orgList) as split
-- this splits the values at the comma and puts them in a temp table
-- then I do a join on the main table and the temp table to do a like on it....
-- but I think it's not right because it's too long.
select something
from maintable gt
join #tempTable tt on gt.org like tt.SearchOrg
where
AYEAR= ISNULL(#year, ayear)
and (AYEAR >= ISNULL(#yearR1, ayear) and ayear <= ISNULL(#yearr2, ayear))
and adate = ISNULL(#Date, adate)
and (adate >= ISNULL(#dateR1, adate) and adate <= ISNULL(#DateR2 , adate))
The final result would be all rows where the maintable.org is 1123, or starts with 223 or starts with 554
The reason for my date craziness is because sometimes the stored procedure only checks for a year, sometimes for a year range, sometimes for a specific date and sometimes for a date range... everything that's not used in passed in as null.
Maybe the problem is there?
Try something like this:
Declare #tempTable Table
(
-- Since the column is a varchar(10), you don't want to use nvarchar here.
SearchOrg varchar(20)
);
INSERT INTO #tempTable
SELECT * FROM dbo.udf_split(#orgList);
SELECT
something
FROM
maintable gt
WHERE
some where statements go here
And
Exists
(
SELECT 1
FROM #tempTable tt
WHERE gt.org Like tt.SearchOrg
)
Such a dynamic query with optional filters and LIKE driven by a table (!) are very hard to optimize because almost nothing is statically known. The optimizer has to create a very general plan.
You can do two things to speed this up by orders of magnitute:
Play with OPTION (RECOMPILE). If the compile times are acceptable this will at least deal with all the optional filters (but not with the LIKE table).
Do code generation and EXEC sp_executesql the code. Build a query with all LIKE clauses inlined into the SQL so that it looks like this: WHERE a LIKE #like0 OR a LIKE #like1 ... (not sure if you need OR or AND). This allows the optimizer to get rid of the join and just execute a normal predicate).
Your query may be difficult to optimize. Part of the question is what is in the where clause. You probably want to filter these first, and then do the join using like. Or, you can try to make the join faster, and then do a full table scan on the results.
SQL Server should optimize a like statement of the form 'abc%' -- that is, where the wildcard is at the end. (See here, for example.) So, you can start with an index on maintable.org. Fortunately, your examples meet this criteria. However, if you have '%abc' -- the wildcard comes first -- then the optimization won't work.
For the index to work best, it might also need to take into account the conditions in the where clause. In other words, adding the index is suggestive, but the rest of the query may preclude the use of the index.
And, let me add, the best solution for these types of searches is to use the full text search capability in SQL Server (see here).