How to batch toggle comment/uncomment certain rows in SQL Server? - sql-server

Is there a way, within SSMS or via a third-party app, to select a batch of lines to toggle their commented status?
Background: in my query, there are two "search modes" that I want to alternate between.
To do this, I need to comment lines 19, 107, 108, 112, and uncomment line 232. Then I need to do the opposite to go back to the other "mode".
Rather than scrolling through the query, is there some way to automate this process?
Example:
1 --select distinct x.name from (
2 select name, dateofbirth, favouritecolour
3 from classmates
4 where dateofbirth between '01-Mar-1990' and '17-Apr-1995'
5 --)x
6 union all
7 --select distinct x.name from (
8 select name, relationship, location
9 from family
10 where relationship = 'uncle'
11 --)x
For full detail, I could have the query like this. If I just wanted the names, I would uncomment lines 1,5,7 and 11.
My real life example is spread across hundreds of lines, and would involve commenting and uncommenting as part of the same "transition"

A simple way to do this is to use a variable:
--Searchmode 0 = Path A
--Searchmode 1 = Path B
DECLARE #SearchMode int = 0 --Change this to change path
IF #SearchMode = 0
BEGIN
SELECT blah
FROM tableA
END
IF #SearchMode = 1
BEGIN
SELECT blah
FROM tableB
END
You could also make it a procedure:
CREATE PROCEDURE dbo.exampleProc #SearchMode int
AS
BEGIN
IF #SearchMode = 0
BEGIN
SELECT blah
FROM tableA
END
IF #SearchMode = 1
BEGIN
SELECT blah
FROM tableB
END
END
Then just execute it and feed in the parameter value like this:
EXEC dbo.exampleProc 0
EXEC dbo.exampleProc 1
Edit:
You could also have the repeated parts of the query always run, then the extra filters only run if #SearchMode = 1. Something like:
DECLARE #SearchMode int = 0
select name, dateofbirth, favouritecolour
into #temp
from classmates
where dateofbirth between '01-Mar-1990' and '17-Apr-1995'
union all
select name, relationship, location
from family
where relationship = 'uncle'
IF #SearchMode = 0
BEGIN
SELECT *
FROM #temp
END
IF #SearchMode = 1
BEGIN
SELECT DISTINCT Name
FROM #temp
END

Related

How can I query properties dynamically in SQL Server?

I am currently working on a project, that includes in an automatical flairing part.
Basicly what this does:
I have a table called Fox, with various columns. And some other tables refering to Fox (i.e. CaughtChickens).
I want to have another table, that I can expand anytime, with 3 columns (other than ID ofc.) in my mind FlairName, FlairColor, and FlairStoredProcedure.
I want to have a stored procedure that returns all FlairName where the FlairStoredProcedure returns 1, for a certain FoxID.
This way I can write a stored procedure that checks if a certain Fox caught a chicken and returns 1 if it did, and add a flair Hunter on the User UI.
There are some cons with this:
Every time I want a new flair I have to write a new stored procedure it (yeah I kinda can't short this one out).
The stored procedures needs to have the same amount of in parameters (ie. #FoxID), and needs to return 1 or 0 (or select nothing when false, select the name if true (?))
I need to use dynamicSQL in the stored procedure that collect these flairs, and I kinda don't want to use any dynamicSQL at all.
Isn't there a lot easier way to do this that I am missing?
EDIT:
Example:
I have a table Fox:
FoxID FoxName FoxColor FoxSize Valid
1 Swiper red 12 1
I would have a table Flairs
FlairID FlairName FlairStoredProcedure Valid
1 Big pFlairs_IsFoxBig 1
2 Green pFlairs_IsFoxGreen 1
I would have 3 stored procedures:
pFox_Flairs
#FoxID int
DECLARE #CurrentFlairSP as varchar(100)
DECLARE #CurrentIDIndex as varchar(100) = 1
DECLARE #ResultFlairs as table(FlairName as varchar(50), FlairColor as integer)
WHILE #CurrentIDIndex <= (SELECT MAX(ID) FROM Flairs WHERE Valid <> 0)
BEGIN
IF EXISTS(SELECT * FROM Flairs WHERE ID = #CurrentIDIndex AND VALID <> 0)
BEGIN
SET #CurrentFlairSP = CONCAT((SELECT TOP 1 FlairStoredProcedure FROM Flairs WHERE ID = #CurrentIDIndex AND VALID <> 0), ' #FoxID=#FoxID')
INSERT INTO #ResultFlairs
EXEC (#CurrentFlairSP)
END
#CurrentIDIndex += 1
END
SELECT * FROM #ResultFlairs
pFlairs_IsFoxBig
#FoxID int
SELECT 'Big' WHERE EXISTS( SELECT TOP 1 * FROM Fox WHERE ID = #Fox AND FoxSize > 10)
pFlairs_IsFoxGreen
#FoxID int
SELECT 'Green' WHERE EXISTS( SELECT TOP 1 * FROM Fox WHERE ID = #Fox AND FoxColor = 'green')
You could create a single table valued function that checks all the conditions:
CREATE OR ALTER FUNCTION dbo.GetFlairs ( #FoxID int )
RETURNS TABLE
AS RETURN
SELECT v.FlairName
FROM Fox f
CROSS APPLY (
SELECT 'Big'
WHERE f.FoxSize > 10
UNION ALL
SELECT 'Green'
WHERE f.FoxColor = 'green'
) v(FlairName)
WHERE f.FoxID = #FoxID;
go
Then you can use it like this:
SELECT *
FROM dbo.GetFlairs(123);
If or when you add more attributes or conditions, simply add them into the function as another UNION ALL

How to extract every 7 characters of an nvarchar into another table?

I have an nvarchar(200) called ColumnA in Table1 that contains, for example, the value:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
I want to extract every 7 characters into Table2, ColumnB and end up with all of these values below.
ABCDEFG
BCDEFGH
CDEFGHI
DEFGHIJ
EFGHIJK
FGHIJKL
GHIJKLM
HIJKLMN
IJKLMNO
JKLMNOP
KLMNOPQ
LMNOPQR
MNOPQRS
NOPQRST
OPQRSTU
PQRSTUV
QRSTUVW
RSTUVWX
STUVWXY
TUVWXYZ
[Not the real table and column names.]
The data is being loaded to Table1 and Table2 in an SSIS Package, and I'm puzzling whether it is better to do the string handling in TSQL in a SQL Task or parse out the string in a VB Script Component.
[Yes, I think we're the last four on the planet using VB in Script Components. I cannot persuade the other three that this C# thing is here to stay. Although, maybe it is a perfect time to go rogue.]
You can use a recursive CTE calculating the offsets step by step and substring().
WITH
cte
AS
(
SELECT 1 n
UNION ALL
SELECT n + 1 n
FROM cte
WHERE n + 1 <= len('ABCDEFGHIJKLMNOPQRSTUVWXYZ') - 7 + 1
)
SELECT substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ', n, 7)
FROM cte;
db<>fiddle
If you have a physical numbers table, this is easy. If not, you can create a tally-on-the-fly:
DECLARE #string VARCHAR(100)='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
--We create the tally using ROW_NUMBER against any table with enough rows.
WITH Tally(Nmbr) AS
(SELECT TOP(LEN(#string)-6) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
SELECT Nmbr
,SUBSTRING(#string,Nmbr,7) AS FragmentOf7
FROM Tally
ORDER BY Nmbr;
The idea in short:
The tally returns a list of numbers from 1 to n (n=LEN(#string)-6). This Number is used in SUBSTRING to define the starting position.
You can do it with T-SQL like this:
DECLARE C CURSOR LOCAL FOR SELECT [ColumnA] FROM [Table1]
OPEN C
DECLARE #Val nvarchar(200);
FETCH NEXT FROM C into #Val
WHILE ##FETCH_STATUS = 0 BEGIN
DECLARE #I INTEGER;
SELECT #I = 1;
WHILE #I <= LEN(#vAL)-6 BEGIN
PRINT SUBSTRING(#Val, #I, 7)
SELECT #I = #I + 1
END
FETCH NEXT FROM C into #Val
END
CLOSE C
Script Component solution
Assuming that the input Column name is Column1
Add a script component
Open the script component configuration form
Go to Inputs and Outputs Tab
Click on the Output icon and set the Synchronous Input property to None
Add an Output column (example outColumn1)
In the Script editor, use a similar code in the row processing function:
Dim idx as integer = 0
While Row.Column1.length > idx + 7
Output0Buffer.AddRow()
Output0Buffer.outColumn1 = Row.
Column1.Substring(idx,7)
idx +=1
End While

How to ensure specific WHERE condition evaluates first on view

Consider the following view:
create view x as
select 1 as [source],* from some_table
union all
select 2,* from some_other_table
When I run
select * from x where source=1
can I be sure that the select 2.... query is not even executed?
The reason is that in my case, that one is an openquery with sluggish speed, which I want to avoid.
You can do stuff like this, but i dont know if that meets your other requirements you might have - Otherwise you can create 2 views.
CREATE proc dbo.usp_selectspecificquery
#source int
AS
BEGIN
IF(#source = 1)
BEGIN
Select 1 as source, * from some_table
END
ELSE
Select 1 as source,* from some_table
union all
select 2 as source,* from some_other_table
END
exec dbo.usp_selectspecificquery 1

Performance issue with larger resultsets MSSQL

I currently have a stored procedure in MSSQL where I execute a SELECT-statement multiple times based on the variables I give the stored procedure. The stored procedure counts how many results are going to be returned for every filter a user can enable.
The stored procedure isn't the issue, I transformed the select statement from te stored procedure to a regular select statement which looks like:
DECLARE #contentRootId int = 900589
DECLARE #RealtorIdList varchar(2000) = ';880;884;1000;881;885;'
DECLARE #publishSoldOrRentedSinceDate int = 8
DECLARE #isForSale BIT= 1
DECLARE #isForRent BIT= 0
DECLARE #isResidential BIT= 1
--...(another 55 variables)...
--Table to be returned
DECLARE #resultTable TABLE
(
variableName varchar(100),
[value] varchar(200)
)
-- Create table based of inputvariable. Example: turns ';18;118;' to a table containing two ints 18 AND 118
DECLARE #RealtorIdTable table(RealtorId int)
INSERT INTO #RealtorIdTable SELECT * FROM dbo.Split(#RealtorIdList,';') option (maxrecursion 150)
INSERT INTO #resultTable ([value], variableName)
SELECT [Value], VariableName FROM(
Select count(*) as TotalCount,
ISNULL(SUM(CASE WHEN reps.ForRecreation = 1 THEN 1 else 0 end), 0) as ForRecreation,
ISNULL(SUM(CASE WHEN reps.IsQualifiedForSeniors = 1 THEN 1 else 0 end), 0) as IsQualifiedForSeniors,
--...(A whole bunch more SUM(CASE)...
FROM TABLE1 reps
LEFT JOIN temp t on
t.ContentRootID = #contentRootId
AND t.RealEstatePropertyID = reps.ID
WHERE
(EXISTS(select 1 from #RealtorIdTable where RealtorId = reps.RealtorID))
AND (#SelectedGroupIds IS NULL OR EXISTS(select 1 from #SelectedGroupIdtable where GroupId = t.RealEstatePropertyGroupID))
AND (ISNULL(reps.IsForSale,0) = ISNULL(#isForSale,0))
AND (ISNULL(reps.IsForRent, 0) = ISNULL(#isForRent,0))
AND (ISNULL(reps.IsResidential, 0) = ISNULL(#isResidential,0))
AND (ISNULL(reps.IsCommercial, 0) = ISNULL(#isCommercial,0))
AND (ISNULL(reps.IsInvestment, 0) = ISNULL(#isInvestment,0))
AND (ISNULL(reps.IsAgricultural, 0) = ISNULL(#isAgricultural,0))
--...(Around 50 more of these WHERE-statements)...
) as tbl
UNPIVOT (
[Value]
FOR [VariableName] IN(
[TotalCount],
[ForRecreation],
[IsQualifiedForSeniors],
--...(All the other things i selected in above query)...
)
) as d
select * from #resultTable
The combination of a Realtor- and contentID gives me a set default set of X amount of records. When I choose a Combination which gives me ~4600 records, the execution time is around 250ms. When I execute the sattement with a combination that gives me ~600 record, the execution time is about 20ms.
I would like to know why this is happening. I tried removing all SUM(CASE in the select, I tried removing almost everything from the WHERE-clause, and I tried removing the JOIN. But I keep seeing the huge difference between the resultset of 4600 and 600.
Table variables can perform worse when the number of records is large. Consider using a temporary table instead. See When should I use a table variable vs temporary table in sql server?
Also, consider replacing the UNPIVOT by alternative SQL code. Writing your own TSQL code will give you more control and even increase performance. See for example PIVOT, UNPIVOT and performance

SQL using UPDLOCK in query to update top 1 record after filtering and ordering table

I have a stored procedure as follows:
CREATE PROCEDURE [dbo].[RV_SM_WORKITEM_CHECKWORKBYTYPE]
(
#v_ServiceName Nvarchar(20)
,#v_WorkType Nvarchar(20)
,#v_WorkItemThreadId nvarchar(50)
)
AS BEGIN
;WITH updateView AS
(
SELECT TOP 1 *
FROM rv_sm_workitem WITH (UPDLOCK)
WHERE stateofitem = 0
AND itemtype = #v_worktype
ORDER BY ITEMPRIORITY
)
UPDATE updateView
SET assignedto = #v_ServiceName,
stateofitem = 1,
dateassigned = getdate(),
itemthreadid = #v_WorkItemThreadId
OUTPUT INSERTED.*
END
It does the job I need it to do, namely, grab 1 record with a highest priority, change it's state from Available(0) to Not-Available(1), and return the record for work to be done with it. I should be able to have many threads (above 20) use this proc and have all 20 constantly running/grabbing a new workitem. However I am finding that beyond 2 threads, addition threads are waiting on locks; I'm guessing the UPDLOCK is causing this.
I have 2 questions, is there a better way to do this?
Can I do this without the UPDLOCK in the cte since the update statement by default uses UPDLOCK? Note, at any given time, there are over 400,000 records in this table.
I had to so something similar once and this is what I would suggest:
AS BEGIN
DECLARE #results table (id int, otherColumns varchar(50))
WHILE (EXISTS(SELECT TOP 1 * FROM #results))
BEGIN
;WITH updateView AS
(
SELECT TOP 1 *
FROM rv_sm_workitem
WHERE stateofitem = 0
AND itemtype = #v_worktype
ORDER BY ITEMPRIORITY
)
UPDATE updateView
SET assignedto = #v_ServiceName,
stateofitem = 1,
dateassigned = getdate(),
itemthreadid = #v_WorkItemThreadId
OUTPUT INSERTED.* into #results
where stateofitem = 0
END
END
This ensures that the call cannot not allow a item to be double processed. (because of the where clause on the update statement).
There are other variations of this idea, but this is an easy way to convey it. This is not production ready code though, as it will continually circle in the while loop until there is something to process. But I leave it to you to decide how to break out or not loop and return empty (and let the client side code deal with it.)
Here is the answer that helped me when I had this issue.

Resources