SQL Server 2012 split out multiple values in one varchar(1200) - sql-server

In a SQL Server 2012 database, I am supposed to count the number of times each 'canned' message is used for elementary students in the last school year and the current school year.
Right now I have the following T-SQL that kind of works:
USE TEST
SELECT
GS.Comments, COUNT(*) AS [Counts]
FROM
dbo.Enrol Enrol
JOIN
dbo.Student Student ON Student.StudentID = Enrol.StudentID
JOIN
dbo.GS GS ON GS.StudentID = Student.Studentid
AND (GS.Comments IS NOT NULL)
AND (GS.Comments <> '')
WHERE
Enrol.grade IN ('KG', '01', '02', '03', '04', '05', '06')
AND Enrol.endYear BETWEEN 2016 AND 2017
GROUP BY
GS.Comments
ORDER BY
Counts DESC, GS.Comments ASC
The problem is the GS.Comments column is defined as varchar(1200). There can be one message in the column and/or there can be lots of messages in this column. Each message ends with a period and there is a space between each message.
An example of multiple messages in the one GS.Comments column would look like the following:
The student is trying hard and needs to make their time more efficiently. This student is good at math. This student turns in their assignments on time. This student seems to enjoy school.
An example of when one messages is in the one GS.Comments column would look like the following:
This student seems to enjoy school.
Thus would show me the T-SQL logic that I can use when the GS.Comments column contains multiple messages and/or just one message so that I can count the number of times each unique message has been used?

You can split your column on periods using the following link. then a simple group by on the newly formed column should let you count it.
Splitting delimited values in a SQL column into multiple rows

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
Alter proc [dbo].[StringSplitIntoRows]
(
#tbl varchar(100),---table name as parameter
#col varchar(100)---column name as parameter
)
As
Begin
--creating two temp tables
If OBJECT_ID('tempdb..#test1') is not null drop table #test1
create table #test1(tempcol varchar(200))
--inserting the table(has comma seperated string column) data into temp table
Declare #tempresult nvarchar(500)
set #tempresult = 'insert into #test1(tempcol) select ' + quotename(#col) + ' from ' + quotename(#tbl)
exec sp_executesql #tempresult
If OBJECT_ID('tempdb..#test2') is not null drop table #test2
create table #test2(tempcol1 varchar(200))
Declare #Name varchar(200)
Declare #real VARCHAR(100)
declare split cursor for ---declared a cursor to fetch row by row data
select tempcol from #test1 --temp table which has comma seperated string in column
open split
fetch next from split into #Name
while (##FETCH_STATUS=0)
Begin
declare #temp int=0
declare #result nvarchar(MAX)=''
declare #begin int=0
while CHARINDEX(',',#Name,#begin) > 0
begin
set #temp=CHARINDEX(',',#Name,#begin)
set #result=SUBSTRING(#Name,#begin,#temp-#begin)
set #begin=#temp+1
insert into #test2(tempcol1) values(#result)
end
set #real = SUBSTRING(#Name,#begin,len(#Name)-abs(#temp-#begin)+1)
insert into #test2(tempcol1) values(#real)
fetch next from split into #Name
End
select distinct tempcol1 from #test2
Close split
Deallocate split
end
GO
--execution
exec StringSplitIntoRows 'YourTableName','ColumnName'

Related

Join a table whose name is stored in the first table

I have a first table [TABLE1] with columns [ID], [Description], [DetailTable]. I want to join [TABLE1] with the [DetailTable]. The name of [DetailTable] is stored in [TABLE1] column.
"SELECT * FROM TABLE1 CROSS JOIN ?????"
Any suggestions?
So... if you cheat and SELECT * from the detailtab, you could do something a bit like this, with dynamic SQL:
-- For the example, choose either 1 or 2 to see the #foo table or the #bar table
DECLARE #Id INT = 1
-- EXAMPLE SCENARIO SETUP --
CREATE TABLE #ListOfTables
( ID INT IDENTITY(1,1) NOT NULL
,[Description] NVARCHAR(255) NOT NULL
,[DetailTable] NVARCHAR(255) NOT NULL);
CREATE TABLE #foo
(Foothing VARCHAR(20));
CREATE TABLE #bar
(Barthing VARCHAR(20));
-- TEST VALUES --
INSERT #ListOfTables VALUES ('foo','#foo'),('bar','#bar');
INSERT #foo VALUES ('A foothing Foothing');
INSERT #bar VALUES ('A barthing Barthing');
-- THE SCRIPT --
DECLARE #SQL NVARCHAR(MAX) = '';
SELECT #SQL =
' SELECT Tab.Id, Tab.[Description], Tab2.*
FROM #ListOfTables AS Tab
CROSS JOIN ' + T.DetailTable + ' AS Tab2
WHERE Tab.Id = ' + CONVERT(VARCHAR(10),#Id)
FROM #ListOfTables T
WHERE T.Id = #Id;
PRINT #SQL
EXEC sp_executesql #SQL;
-- CLEAN UP --
DROP TABLE #ListOfTables;
DROP TABLE #bar;
DROP TABLE #foo;
However, I have to agree with the comments that this is a pretty nasty way to do things. If you want to choose particular columns and the columns are different for each detail table, then things will start to get really unpleasant... Does this give you something you can start with?
Remember, the best solution will almost certainly involve redesigning things so you don't have to jump through these hoops!
All of the detail tables must have identical schema.
Create a view that unions all the tables
CREATE VIEW vAllDetails AS
SELECT 'DETAIL1' table_name, * from DETAIL1
UNION ALL
SELECT 'DETAIL2' table_name, * from DETAIL2
UNION ALL
SELECT 'DETAIL3' table_name, * from DETAIL3
When you join against this view, SQL Server can generate a plan that uses a "startup predicate expression". For example, a plan like this: sample plan. At first glance, it looks like SQL is going to scan all of the detail tables, but it won't. The left most filters include a "startup predicate", so for each row we read from table1, only if TableName matches will that branch be executed.

Execute 2 queries that are in 2 variables and store the result in other 2 variables

As mentioned in the question, I want to store the results of these queries into other variables so that I can compare the output and find the ones which are not matching. Please help me out with it. The variable #Stagename consists of the first query and variable #correctname consists of the second query. I found some answers of storing them into a table variable but it is not working. These queries are not a single query and hence are stored in the form of rows of a table and are being fetched by the cursor. I've passed the second variable #tablename which I want as the final output but only of the ones in whom the comparison is not matching. I've used the following code:
DECLARE #Stagename VARCHAR(MAX)
DECLARE #correctname VARCHAR(MAX)
DECLARE #tablename VARCHAR(MAX)
--DECLARE #StageCount VARCHAR(max)
--DECLARE #IndexCount VARCHAR(MAX)
DECLARE #Table1 TABLE (StageCount Varchar(max), TableName VARCHAR(MAX))
DECLARE #Table2 TABLE (IndexCount Varchar(max), TableName VARCHAR(MAX))
--DEALLOCATE IF EXISTS CS_StagingIndex
DECLARE CS_StagingIndex CURSOR FOR
SELECT StageCount, CorrectCount, TableName FROM bak.StagingindexesQuery
OPEN CS_StagingIndex
FETCH NEXT FROM CS_StagingIndex
INTO #Stagename,#Correctname,#tablename
WHILE ##Fetch_Status = 0
BEGIN
INSERT INTO #Table1(StageCount,TableName) VALUES (exec(#StageName),#tablename);
INSERT INTO #Table2(IndexCount,TableName) VALUES (exec(#CorrectName),#tablename);
--Select * from #Table1 Ignore this.
FETCH NEXT FROM CS_StagingIndex
INTO #Stagename,#Correctname,#tablename
END
CLOSE CS_StagingIndex
DEALLOCATE CS_StagingCursor
Select count(1) from stg.LogisticsElectronicAddres - This is the query stored in #StageName.
select count(1) from (select distinct recid1 from stg.LogisticsElectronicAddress) x - This is the query stored in #IndexName.
LogisticsElectronicAddress and this is the tablename.
Now if for example, the result of StageName query is 2000 and the result of Correctname is also 2000, then the tablename should not be printed. But if the results dont match, then I want the tablename to be printed. There are multiple rows in bak.StagingIndexesQuery table that contain such queries for multiple tables.
I don't believe that's the correct EXEC syntax for sql-server; I don't think that is going to work.
What you can do is use the statement .. INSERT table EXEC storedprocName.
However, there are constraints - the table layout must match that of the return procedure in terms of column count/order/data types/length ( within reason, ie if a column in the table is NVARCHAR(100) and the stored procedure returns that column as NVARCHAR(105), that should be fine - unless of course the data itself exceeds the 100 length). I believe column names are ignored for INSERT/EXEC
Also, if the query being executed has an INSERT/EXEC , this will not work (only one allowed at anyone time)
So you will need to use dynamic SQL...
DECLARE #MySQLStatement NVARCHAR(400)
DECLARE #MyStoedProcName NVARCHAR(100)
DECLARE #Table1 TABLE (StageCount Varchar(max), TableName VARCHAR(MAX))
SET #MySQLStatement = 'INSERT #Tablename EXEC ' + #StoredProcedure
EXEC sp_ExecuteSQL #MySQLStatement
Now off the top of my head, I can't remember if that #Table1 will be in the scope of that dynamic SQL statement. If it isn't, make #Table1 a temp table (ie create table #table1 [ ... etc ] )

Add column into Table through variable using SQL Server

I have check unique value in the column VariantSKU using this sql code
alter Proc spIsUnique
#columnname nvarchar(max),
#tablename nvarchar(max)
As
Begin
EXEC ('select '+#columnname+',
IIf (count(*)>1,''False'',''True'') as Total
from '+#tablename+'
group by '+#columnname)
End
As you can see new column Total which contain True or False..
Now I want to add this column into the table in database. USing function it was not possible so I have created new table exactly same data called "Result" table.How can I add that column Total in Result table.
How can I do it?
if i understand well your question then use temp db to stock your table then join it with the destination table or do juste a union
alter Proc spIsUnique
#columnname nvarchar(max),
#tablename nvarchar(max)
As
Begin
= EXEC ('select '+#columnname+',
IIf (count(*)>1,''False'',''True'') as Total
into ##tempdb
from '+#tablename+'
group by '+#columnname)
End
select d.* , t.Total from destinationtable as d
inner join ##tempdb as t ON d.#columnname = t.#columnname

How to INSERT INTO table column with string/variable

This is the data I have pulled from powershell and inserted it into a #temptable:
Name : SULESRKMA
Location : Leisure Services - Technology Services
Shared : False
ShareName :
JobCountSinceLastReset : 0
PrinterState : 131072
Status : Degraded
Network : False
I'm while looping through the data and have stripped the values from the identifiers. I'd like to use these identifiers to insert the values into a table with identical Column names to the identifiers. So for example, I have a variable called #identifier = "Name" and a temp table #printers with a column name of Name. I'd like to do something like:
SELECT --select statement
INSERT INTO #printers(#identifier)
But This doesn't seem to work, unsurprisingly. Is there a way to accomplish this? (The #identifier variable will be changing to the other identifiers in the data throughout the course of the while loop.)
Any alternate suggestions that don't even involve using this sort of method are welcome. My ultimate goal is just to get this data as a row into a table.
(I'm currently using Microsoft SQL Server Management Studio if that matters)
First, it's unlikely you need to loop over anything in this situation. Think set based operations when you think about SQL.
INSERT INTO #temptable (Column1Name, Column2Name, Column3Name)
VALUES (#identifer, #anotherIdentifier, #someOtherIdentifier)
--optional clauses
WHERE Column1Name = 'some value' OR Column1Name = #someIdentifier
Or you can SELECT INTO
SELECT
#identifier,
#anotherIdentifer,
#someOtherIdentifier
INTO #temptable
It's important that you have a value in your SELECT INTO for each column in the table which you are trying to add the data to. So, for example, if there were 4 columns in #temptable and you only had 3 values to insert (columns 1, 2 , and 3) then you'd need to NULL column 4 or set it statically.
SELECT
#identifier,
#anotherIdentifer,
#someOtherIdentifier,
NULL
INTO #temptable
--or
SELECT
#identifier,
#anotherIdentifer,
#someOtherIdentifier,
'static value'
INTO #temptable
EDIT
If you want to use a varible to speciy the column that you want to insert into, you have to use dynamic sql. Here is an example:
if object_id ('tempdb..#tempTable') is not null drop table #tempTable
create table #tempTable (Column1Name int, Column2Name int, Column3Name int)
declare #columnName varchar(64) = 'Column1Name'
declare #sql varchar(max)
set #sql =
'insert into #tempTable (' + #columnName + ')
select 1'
exec(#sql)
select * from #tempTable

In SQL Server, is there a way to avoid using a Cursor?

I have a table where each record has a Table_Name (name of a table). I then use a Cursor to select all table names related to some record in to a Cursor. Then I do a WHILE for each table name in the Cursor to do some job.
I want to know if it's possible to solve this problem without using a Cursor.
DECLARE tables_cursor CURSOR FAST_FORWARD FOR SELECT Table_Name FROM Some_Table WHERE ...
FETCH NEXT FROM tables_cursor INTO #Dynamic_Table_Name
WHILE ##FETCH_STATUS = 0
BEGIN
...
END
Foreach table name in the cursor I do a dynamic SQL query like this:
SELECT #sql = '
UPDATE dbo.' + #Dynamic_Table_Name + '
SET ...'
EXEC sp_executesql #sql, #params, ...
My question is this: Is it possible to avoid using Cursor to solve this problem?
Unfortunately the design of having table name to reference a table can't be changed, of which I would have done immediately if I could.
yes, you can solve this problem without using cursor. Instead you need to introduce the new table which stores the table name from actual table along with auto generated id column.
Check out the below sample query
declare #test table (id int identity,tableName varchar(20))
insert into #test
select 'abc' union all
select '123' union all
select '345' union all
select 'sdf' union all
select 'uhyi'
instead above query, you can use your query to populate the table variable
insert into #test
SELECT Table_Name FROM Some_Table WHERE ...
And
--select * from #test
declare #cnt int
declare #incr int
select #cnt = count(id) from #test
set #incr = 1
while (#incr <= #cnt)
begin
select tableName from #test where id = #incr
set #incr =#incr + 1
end
Yes, you could avoid the cursor, but you can't avoid the dynamic queries.
You could possibly make a query that returns all the dynamic queries concatenated together as a single string. That way you could execute them all without using a loop, but that's not really any better...
If you can't change the database design, you are stuck with dynamic queries.
Well, you can hide the use of a cursor by using the (undocumented, but widely-used) MS stored procedure sp_MSforeachdb (Google has lots of examples); but that uses a cursor internally, so if it's a philosophical objection then that doesn't really help.
I don't think there can be a set-based way to do this kind of thing, since each table probably has a different relational structure.

Resources