Using a distinct value from table as an Alias - sql

I've got a view that is a bunch of survey responses. The structure of which is something like the below (excluding the Q1Text and Q2Text).
Q1 | Q1Text | Q2 | Q2Text
Value1 | Q1Header | Value1 | Q2Header
Value1 | Q1Header | Value2 | Q2Header
Value3 | Q1Header | Value3 | Q2Header
What I would like is for the Q1Text, which is a grabbing it's values from a seperate field, to be the alias for Q1.
The values for Q1Text is the same value regardless of how many rows there are, and the same for Q2Text
I was told by a colleague to try dynamic SQL, but not sure if my lack of understanding wasn't allowing me to do it or what, but one of my many attempts of this is below. Might give you a better idea if what i'm after.
DECLARE #table NVARCHAR(128)
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Q1 NVARCHAR(128)
SET #table = '[MyTable]'
SET #Q1 = 'Select distinct P1Q1Qtext from' + #table
SET #sql = 'Select P1Q1 as ' + #Q1 + ' FROM ' + #table
EXEC sp_executesql #sql
Thanks in advance.

You were storing query in variable #Q1 but not executing it.
DECLARE #table NVARCHAR(128)
DECLARE #SQL NVARCHAR(MAX)
DECLARE #Q1 NVARCHAR(128)
SET #table = '[MyTable]'
SET #Q1 = (Select distinct top 1 P1Q1Qtext from MyTable)
SET #sql = 'Select P1Q1 as [' + #Q1 + '] FROM ' + #table
EXEC sp_executesql #sql

Related

Replacing more than 1 period to 1 period in sql

I have the following code to convert more than one period to one period in a column of a table.
alter proc replace_characters_1
#COLUMN_NAME varchar(30),
#TABLE_NAME varchar(30)
as
declare #SQL varchar(MAX)
while #COLUMN_NAME like '%..%'
begin
set #SQL= 'update [' +#TABLE_NAME+ '] set [' +#COLUMN_NAME+ '] = replace([' +#COLUMN_NAME+ '],''..'',''.'')';
exec(#SQL)
end
I want to change the Anna...Amal to Anna.Amal with one go, but the loop is not working. What should I do?`
One possible approach is to use nested REPLACE()s:
SET ColumnName = REPLACE(REPLACE(REPLACE(ColumnName, '.', '<>'), '><', ''), '<>', '.')
After the first REPLACE() the part from the text that contains periods (.) looks like <><><>. After the second REPLACE() the result is only <> and the final REPLACE() returns single period (.). If the characters < and > exist in the input text, you can choose another pair of characters.
Table:
CREATE TABLE Data (Name varchar(100))
INSERT INTO Data (Name)
VALUES
('ANNA..Amal'),
('ANNA..Amal.'),
('ANNA.Amal.'),
('ANNA...........Amal.'),
('ANNA.....Amal')
Procedure:
CREATE PROC replace_characters_1
#COLUMN_NAME sysname,
#TABLE_NAME sysname
AS
BEGIN
DECLARE #SQL nvarchar(MAX)
DECLARE #RC int
SET #SQL =
N'UPDATE ' + QUOTENAME(#TABLE_NAME) + N' ' +
N'SET ' + QUOTENAME(#COLUMN_NAME) + N' = ' +
N'REPLACE(REPLACE(REPLACE(' + QUOTENAME(#COLUMN_NAME) + ', ''.'', ''<>''), ''><'', ''''), ''<>'', ''.'') ' +
N'WHERE ' + QUOTENAME(#COLUMN_NAME) + N' LIKE ''%..%'''
EXEC #RC = sp_executesql #SQL
RETURN #RC
END
Result:
EXEC replace_characters_1 N'Name', N'Data'
SELECT * FROM Data
Name
ANNA.Amal
ANNA.Amal.
ANNA.Amal.
ANNA.Amal.
ANNA.Amal
Here is an approach that will reduce repeating characters.
Example
Declare #YourTable Table ([SomeCol] varchar(50))
Insert Into #YourTable Values
('Anna...Amal')
,('Anna........Amal')
,('Anna.Amal')
,('Anna Amal')
Select *
,NewVal = replace(replace(replace(SomeCol,'.','†‡'),'‡†',''),'†‡','.')
from #YourTable
Returns
SomeCol NewVal
Anna...Amal Anna.Amal
Anna........Amal Anna.Amal
Anna.Amal Anna.Amal
Anna Amal Anna Amal
Please check Zhorov's answer as it avoids multiple operations like this one.
CREATE PROCEDURE replace_characters_1
#COLUMN_NAME varchar(30),
#TABLE_NAME varchar(30)
AS
BEGIN
DECLARE #SQL NVARCHAR(MAX) = N'
UPDATE T SET
' + QUOTENAME(#COLUMN_NAME) + N' = REPLACE(' + QUOTENAME(#COLUMN_NAME) + N',''..'',''.'')
FROM
' + QUOTENAME(#TABLE_NAME) + N' AS T
WHERE
T.' + QUOTENAME(#COLUMN_NAME) + N' LIKE ''%..%'';
SET #UpdatedRowsOut = ##ROWCOUNT;';
DECLARE #UpdatedRows INT = 1;
WHILE #UpdatedRows > 0
BEGIN
EXECUTE sp_executesql
#SQL,
N'#UpdatedRowsOut INT OUTPUT',
#UpdatedRowsOut = #UpdatedRows OUTPUT;
END
END
Dynamic SQL is now returning the amount of rows that were updated, so it keeps going as long as there are values with .. for that column (note that there is a WHERE filter, you don't want to update all rows every time!).
SQL Server isn't the best for regex expressions, maybe you want to consider using a CLR function if you need to do different kind of stuff with regex.
I am using CHARINDEX and STUFF to derive the resultset
DECLARE #sqlstring Varchar(max) = 'Anna...Amal'
-- Getting first index of .
DECLARE #firstidx INT = CHARINDEX('.',#sqlstring)
-- Getting last index of .
Declare #lastidx int = (LEN(#sqlstring) - CHARINDEX('.',REVERSE(#sqlstring))) + 1
-- Stuffing the gap with emptystring
SELECT STUFF(#sqlstring,#firstidx+1,(#lastidx-#firstidx),'') as result
Result
+-----------+
| result |
+-----------+
| Anna.Amal |
+-----------+
UPDATE: If there are multiple comma separated values
DECLARE #sqlstring Varchar(max) = 'Anna...Amal,Vimal...Mathew'
SELECT STRING_agg(removedvalues,',') as values
FROM
(SELECT STUFF(value, CHARINDEX('.',value)+1
,(LEN(value) - CHARINDEX('.',REVERSE(value)) + 1) - CHARINDEX('.',value),'') AS removedvalues
FROM string_split(#sqlstring,',') ) AS t
+------------------------+
| Values |
+------------------------+
| Anna.Amal,Vimal.Mathew |
+------------------------+

Transposing a SQL query result

I have a query that produces a table that looks like this:
+----------------+-----------+------------+----------+
| CustomerNumber | FirstName | MiddleName | LastName |
+----------------+-----------+------------+----------+
| 123456 | Test | Test1 | Test2 |
+----------------+-----------+------------+----------+
What I am needing is for the table to look like this:
+----------------+--------+
| CustomerNumber | 123456 |
+----------------+--------+
| FirstName | Test |
| MiddleName | Test1 |
| LastName | Test2 |
+----------------+--------+
This is my current sql query:
SELECT
CustomerNumber,
FirstName,
MiddleName,
LastName
FROM Customers
WHERE CustermerNumber = 123456
Is there a way to complete this? I have been looking at transposing it via unpivot but have not been able to understand how.
You can use apply :
select tt.*
from table t cross apply (
values ('CustomerNumber', CustomerNumber), ('FirstName', FirstName),
('MiddleName', MiddleName), ('LastName', LastName)
) tt (name, v);
With dynamic TSQL pivoting you can also manage multiple rows input:
if OBJECT_ID('Test') is not null
drop table [dbo].[Test]
CREATE TABLE [dbo].[Test](CustomerNumber varchar(10), FirstName varchar(10),
MiddleName varchar(10), LastName varchar(10))
--populate test table
insert into [dbo].[Test] values
(123456, 'Test','Test1','Test2')
, (234567, 'Test_2','Test_21','Test_22')
, (345678, 'Test_3','Test_31','Test_32')
--this variable holds all the customer numbers that will become column names
declare #columns nvarchar(max)=''
--this variable contains the dinamically generated TSQL code
declare #sql nvarchar(max)=''
select #columns = #columns + ', [' + [CustomerNumber] + ']' from [dbo].[Test]
set #columns = RIGHT(#columns, len(#columns)-2)
set #sql = #sql + 'select piv.COL as CustomerNumber, ' + #columns
set #sql = #sql + ' from '
set #sql = #sql + ' ( '
set #sql = #sql + ' select [CustomerNumber], col, val, ord '
set #sql = #sql + ' from [dbo].[Test] '
set #sql = #sql + ' CROSS APPLY ('
set #sql = #sql + ' VALUES (''FirstName'' ,FirstName , 1), '
set #sql = #sql + ' (''MiddleName'',MiddleName, 2), '
set #sql = #sql + ' (''LastName'' ,LastName, 3) '
set #sql = #sql + ' )CS (COL,VAL,ORD) '
set #sql = #sql + ' ) src '
set #sql = #sql + ' pivot ( max(val) for [CustomerNumber] in ('+#columns+') ) piv'
set #sql = #sql + ' order by ord'
exec(#sql)
Sample input and output with one row:
Sample input and output with three rows:

Dynamic tSQL Query from parameter

I'm struggling with a dynamic query in tSQL to be used with Excel ODBC.
SQL Server 2016 via ODBC Driver 11 for SQL Server
I found this article but it only uses the table name stored in a parameter, I'd need the actual table to be within parameter: Simple dynamic TSQL query syntax
declare #t table(value1 int, value2 nvarchar(1024))
insert #t SELECT 1 as value1, 'test' as value2
declare #where nvarchar(max)
declare #query nvarchar(max)
declare #sql nvarchar(max)
set #where = ' WHERE value1 = 1'
set #query = 'Select * from #t'
set #sql = #query + #where
EXEC(#sql)
This results in the error message Must declare the table variable "#t"
Unfortunately I can't use temporary tables as the connector doesn't support temp tables.
My original query is far more complex and contains 6 different parameters all being injected at different points into the query and 2 table-parameters that hold temp results
Thanks in advance
You just need to declare your table as part of your query. The table is declared and recognized in that scope:
declare #t table(value1 int, value2 nvarchar(1024))
insert #t select 1 as value1, 'test' as value2
declare #where nvarchar(max)
declare #query nvarchar(max)
declare #sql nvarchar(max)
set #where = ' where value1 = 1'
set #query = 'declare #t table(value1 int, value2 nvarchar(1024))
insert #t select 1 as value1, ''test'' as value2
select * from #t'
set #sql = #query + #where
exec(#sql)
Result:

Select from table with dynamic compute name

I want to write query in which name table will dynamicaly compute. I have code like this below. What should I put in 'magic code' region?
DECLARE #myTableName nvarchar(100) = 'sch'
+ CAST(#nowYear as VARCHAR(5))
+ 'Q'
+ CAST(#nowQuarter as VARCHAR(3))
+ '.[tVisits]'
-- magic code --
myTable = DoSomething(#aktTableName)
-- magic code --
SELECT * FROM myTable
I use MS SQL Server 2012
You need use the dynamic SQL -
DECLARE
#nowYear INT = 2013
, #nowQuarter INT = 1
DECLARE #myTableName NVARCHAR(100) = '[sch'
+ CAST(#nowYear AS VARCHAR(5))
+ 'Q'
+ CAST(#nowQuarter AS VARCHAR(3))
+ '].[tVisits]'
DECLARE #SQL NVARCHAR(MAX) = N'SELECT * FROM ' + #myTableName
EXEC sys.sp_executesql #SQL
Instead of SELECT * FROM myTable
You need to do something like
DECLARE #sql nvarchar(4000)
SELECT #sql = ' SELECT * FROM ' + #myTable -- #myTable is a string containing qualified table name
EXEC sp_executesql #sql
Note that sp_executesql allows for a parameterized query - check its documentation

Select all columns except those with only null values

Is there a way to select the column names of a certain table except those columns with only null values without knowing how many columns the table have.
-------------------------
| col1 | col2 | col3 |
------------------------
| val1 | null | val2 |
| val1 | null | null |
| null | null | val2 |
-------------------------
Should result in:
------------------------------------
| cols_except_those_with_null_only |
-----------------------------------
| col1 |
| col3 |
------------------------------------
Thanks!
Create a stored procedure with following content:
create table #cols (colname varchar(255), nullCount int)
insert into #cols (colname)
select name from syscolumns where id = object_id('tblTest')
declare #c varchar(255)
declare curCols cursor for select colname from #cols
open curCols
fetch next from curCols into #c
while ##fetch_status = 0 begin
exec ('update #cols set nullCount = (select count(*) from tblTest where ' + #c + ' is not null) where colname = ''' + #c + '''')
fetch next from curCols into #c
end
close curCols
deallocate curCols
declare #rv table (cols_expect_those_with_null_only varchar(255))
insert into #rv (cols_expect_those_with_null_only)
select colname from #cols
where nullCount > 0
drop table #cols
select * from #rv
Try this, it's not the tidiest but will work, just set #Table to your table name.
DECLARE #Table AS VARCHAR(100)
SET #Table = 'Example'
DECLARE #TempColumn VARCHAR(100)
DECLARE #Sql NVARCHAR(300)
DECLARE #HasNoNulls INT
CREATE TABLE #Columns (
ColumnName VARCHAR(100)
)
DECLARE ColumnCursor CURSOR FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.Columns
WHERE TABLE_NAME = #Table
OPEN ColumnCursor
FETCH NEXT FROM ColumnCursor
INTO #TempColumn
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'SELECT #HasNoNullsOut = COUNT(*) FROM ' + #Table + ' WHERE ' + #TempColumn + ' IS NOT NULL'
PRINT #SQL
EXECUTE sp_executesql #SQL, N'#HasNoNullsOut int OUTPUT', #HasNoNullsOut=#HasNoNulls OUTPUT
IF #HasNoNulls > 0
BEGIN
INSERT INTO #Columns
VALUES(#TempColumn)
END
FETCH NEXT FROM ColumnCursor
INTO #TempColumn
END
CLOSE ColumnCursor
DEALLOCATE ColumnCursor
SELECT * FROM #Columns
DROP TABLE #Columns
With this structure you can do a query in a store procedure that allows you to ask for each column name of the table and if it has null values without caring how many columns your table has
SELECT a.[name] as 'Table',
b.[name] as 'Column'
FROM sysobjects a
INNER JOIN syscolumns b
ON a.[id] = b.[id]
where table='yourtable'