SQL add conditional query - sql-server

In a table in my database I have 12 columns, month_1 .... month_12.
I want to add conditional to my SQL query, if #m = 1 will do
CREATE PROCEDURE XemDiem_Top5Month1
#m INT
AS
BEGIN
SELECT *
FROM XemDiem
WHERE (month_1 IN (SELECT TOP (5) month_1
FROM XemDiem
GROUP BY month_1
ORDER BY month_1DESC))
END
if #m = 2 will do
CREATE PROCEDURE XemDiem_Top5Month1
#m INT
AS
BEGIN
SELECT *
FROM XemDiem
WHERE (month_2 IN (SELECT TOP (5) month_2
FROM XemDiem
GROUP BY month_2
ORDER BY month_2 DESC))
END
and so on ....
I don't want to write too many queries, so please help

Assign the select query to a string and concatenate your parameter. Finally execute the string.
Create proc XemDiem_Top5Month1
#m int
AS
BEGIN
Delcare #query nvarchar(max)
set #query='SELECT *
FROM XemDiem
WHERE
(
month_''+Convert(varchar(10),#m)+'' IN
(
SELECT TOP (5) month_''+Convert(varchar(10),#m)+''
FROM XemDiem
GROUP BY month_''+Convert(varchar(10),#m)+''
ORDER BY month_''+Convert(varchar(10),#m)+'' DESC
)
)'
execute(#query)

declare #ishistoric varchar(100) ='month_1'
(SELECT *
FROM XemDiem
WHERE
(
CASE #ishistoric
WHEN 'month_1' THEN month_1
WHEN 'month_2' THEN month_2
...
WHEN 'month_12' THEN month_12
END IN
(
SELECT TOP (5) CASE #ishistoric
WHEN 'month_1' THEN month_1
WHEN 'month_2' THEN month_2
...
WHEN 'month_12' THEN month_12
END
FROM OSUSR_KIA_PRINTSTICKER
GROUP BY CASE #ishistoric
WHEN 'month_1' THEN month_1
WHEN 'month_2' THEN month_2
...
WHEN 'month_12' THEN month_12
END
ORDER BY CASE #ishistoric
WHEN 'month_1' THEN month_1
WHEN 'month_2' THEN month_2
...
WHEN 'month_12' THEN month_12
END DESC
)
))
Make your remain query just like that and query is based on the provided columns & it will the return respective records for specific column which are passed into the parameter.

Related

SQL Server union temp tables

I have a piece of SQL that takes a parameter, assesses the contents of the parameter and depending on the contents applies different UDF to the input parameter.
the code goes something like this:
declare #order varchar(50) = 's12345..s12347'
if isnull( CHARINDEX('.',#order),0) >0
begin
select n as order_no into #temp
FROM F_GetAllNBetween(#order)
end
else if ( isnull( CHARINDEX(',',#order),0) >0 )
begin
select [value] as order_no into #temp2
FROM dbo.F_SplitList(#order,',')
end
if OBJECT_ID('tempdb..#temp') is not null
select * from #temp where order_no <>''
if OBJECT_ID('tempdb..#temp2') is not null
select * from #temp2 where order_no <>''
what I want to do is, get the output from the above union into another temp table and use it in the where clause of the rest of the code.
I cannot use union as it errors saying a select is expected.
I cannot use cte as I cannot use the check to see if the temp tables are empty
I cannot ignore checking for empty temp tables else it errors saying object does not exist ( if statement only creates one of the two temp tables)
I cannot figure out how to get the output to a different temp table. Any ideas suggestions or better way of doing the same please?
As mentioned in my comment, you can simplify your current code to achieve what you want with a UNION and WHERE clauses to replace the IF statements. e.g.
SELECT order_no
--INTO #someTempTable -- if needed
FROM (
SELECT order_no = n
FROM dbo.F_GetAllNBetween(#order)
WHERE CHARINDEX('.', #order) > 0
UNION ALL
SELECT value
FROM dbo.F_SplitList(#order,',')
WHERE CHARINDEX(',', #order) > 0
) AS T
WHERE order_no <> '';

How to UPDATE TOP(n) with ORDER BY giving a predictable result?

I'm trying to read the top 100 items of a database table that is being used like a queue. As I do this I'm trying to mark the items as done like this:
UPDATE TOP(#qty)
QueueTable WITH (READPAST)
SET
IsDone = 1
OUTPUT
inserted.Id,
inserted.Etc
FROM
QueueTable
WHERE
IsDone = 0
ORDER BY
CreatedDate ASC;
The only problem is, according to UPDATE (Transact-SQL) on MSDN, the ORDER BY is not valid in an UPDATE and:
The rows referenced in the TOP expression used with INSERT, UPDATE, or
DELETE are not arranged in any order.
How can I achieve what I need which is to update the items at the top of the queue while also selecting them?
SQL Server allows you to update a derived table, CTE or view:
UPDATE x
SET
IsDone = 1
OUTPUT
inserted.Id,
inserted.Etc
FROM (
select TOP (N) *
FROM
QueueTable
WHERE
IsDone = 0
ORDER BY
CreatedDate ASC;
) x
No need to compute a set of IDs first. This is faster and usually has more desirable locking behavior.
Tested in SSMS, it works fine. You may need to do some modification accordingly.
--create table structure
create table #temp1 (
id int identity(1,1),
value int
)
go
--insert sample data
insert #temp1 values (1)
go 20
--below is solution
declare #qty int = 10
declare #cmd nvarchar(2000) =
N'update #temp1
set value= 100
output inserted.value
where id in
(
select top '+ cast(#qty as nvarchar(5)) +' id from #temp1
order by id
)';
execute sp_executesql #cmd
You can use ranking function (for example row_number).
update top (100) q
set IsDone = 1
output
inserted.Id,
inserted.Etc
from (
select *, row_number() over(order by CreatedDate asc, (select 0)) rn
from QueueTable) q
where rn <= 100

Based on a variable: SQL Query to fetch data for multiple values from one column OR get all values from that column

Problem Statement :
when #a has a single word(Ex. 'name1') OR comma separated string (Example 'name1,name2,name3') then the query should return the manager names of employees with name1 and name2 and name3
when #a has an empty string then return the manager names of all the employees in the emp_master table
I have defined a stored procedure where I pass a variable.
This variable can be a comma separated string, a single word or an empty string.
If the string is comma separated then I split that string and get values based on the return table of split statement
else
I get the related value of the non comma separated data using normal subquery
I have tried to achieve this in the following way
Declare #a varchar(50)= ''
select emp.Name from
emp_master emp
where
(LEN(#a)=0 AND emp.Name in
(
SELECT DISTINCT [Name] FROM
[dbo].[Emp_Master] WHERE [EmpId] IN
(
SELECT
DISTINCT [MGR_ID]
FROM [dbo].[Emp_Master]
)
)
)
OR
emp.Name in (Select * from [dbo].[SplitString](#a, ','))
Details for the above sample:
[dbo].[SplitString] - custom written function : returns a table of split values. So
Select * from [dbo].SplitString
will return
SplitTable
----------
name1
name2
name3
and
Select * from [dbo].[SplitString](',','name1')
will return
SplitTable
----------
name1
[dbo].[Emp_Master] contains data for all the employees
[MGR_ID] is the column which has the employeeID of the employee manager
#a is the input variable
The Database is MS SQL 2008
My current solution(the above insane query) solves my purpose but it is very slow, it would be helpful to get an optimized and faster working solution for the problem
Emp_master Table has 400 000 rows, 30 columns
There are 18 000 managers in that table
CREATE NONCLUSTERED INDEX ix ON dbo.Emp_Master ([MGR_ID])
GO
DECLARE #a VARCHAR(50) = ''
DECLARE #t TABLE (val VARCHAR(50) PRIMARY KEY WITH(IGNORE_DUP_KEY=ON))
INSERT INTO #t
SELECT item = t.c.value('.', 'INT')
FROM (
SELECT txml = CAST('<r>' + REPLACE(#a, ',', '</r><r>') + '</r>' AS XML)
) r
CROSS APPLY txml.nodes('/r') t(c)
SELECT /*DISTINCT*/ [Name]
FROM dbo.Emp_Master e1
WHERE (
#a = ''
AND
e1.[EmpId] IN (SELECT DISTINCT MGR_ID FROM dbo.Emp_Master)
)
OR (
#a != ''
AND
e.Name IN (SELECT * FROM #t)
)
OPTION(RECOMPILE)
TRY THIS
CREATE NONCLUSTERED INDEX IX_MGR_ID_Emp_Master ON dbo.Emp_Master ([MGR_ID])
GO
Create Procedure searchname (#a varchar(255))
as
IF (#a = '')
BEGIN
EXEC Searchname1 #a
END
ELSE
BEGIN
EXEC Searchname2 #a
END
GO
Create Procedure Searchname1 (#a varchar(255))
AS
SELECT DISTINCT [Name] FROM
[dbo].[Emp_Master] m1 WHERE
exists
(
SELECT
*
FROM [dbo].[Emp_Master] m2
WHERE
m1.[EmpId]= m2.[MGR_ID]
)
GO
Create Procedure Searchname2 (#a varchar(max))
AS
Select #a = ' SELECT '''+replace( #a,',',''' Union ALL SELECT ''')+' '''
Create table #names (name varchar(255))
insert into #names
EXEC ( #a )
select emp.Name from
emp_master emp
WHERE
emp.Name in( Select name FRom #names)
option (recompile)
IF YOU ARE ALREADY DEALING WITH SQL INJECTION AT APPLICATION LEVEL
THEN
ALTER procedure [dbo].[Searchname2] (#a varchar(max))
AS
select #a = ''''+replace ( #a,',',''',''')+''''
DECLARE #sql NVARCHAR(MAX) = N'
select distinct emp.Name from
emp_master emp
WHERE
emp.Name in( '+#a+')'
EXEC (#sql)

COUNT in CASE - SQL Server

Is it possible to do a value assignment with a COUNT() as the when clause?
Like so:
SELECT #value =
CASE
WHEN
COUNT(tableID)
FROM (SELECT TOP (5) tableID FROM table) AS id = 20
THEN 'Looks Good'
END
I basically what to select a variable amount of rows [TOP (#rowCount)], then take action based on the number of rows counted. I'm sure I can do this someway somehow, guessing I'm just missing something in the syntax.
If you're looking for code branching, the following would work:
IF 20 = (select count(*)
from (select top (5) tableID from table) as id)
PRINT 'Looks Good'
ELSE
PRINT '5 will never equal 20'
If you want to get or set a value, one of the following would work:
SELECT case count(*)
when 20 then 'good'
else 'bad'
end
from (select top (5) tableID from table) as id
or
SELECT case
when count(*) > 5 then 'Over 5'
when count(*) < 5 then 'Under 5'
else 'Exactly 5'
end
from (select top (5) tableID from table) as id
Not sure if I understand the question, but maybe try something like
select #val = case when the_number >= 20 then 'Looks good' end
from (
select count(*) the_number from some_table
) x
Assuming you are using at least sql2005 or greater then this will work -
--create a table to test with
create table #TestTable
(
TestTableID int primary key
)
--populate test table
declare #i int = 0;
while #i < 10
begin
insert into #TestTable select #i;
set #i = #i + 1;
end
GO
--now create variables to hold the TOP value and to store the result
declare #a int = 5
,#value varchar(10);
--correct case stmt syntax
set #value = case
when (select count(RecordList) as 'RecordListCount' from (select top (#a) TestTableID as 'RecordList' from #TestTable) as sq) = 20 then 'Looks Good'
else 'Looks Bad'
end;
select #value;
Remember to put the TOP variable in parentheses and to supply aliases for all of the tables and columns.
I hope that helps!
I think I understood your question. You want to know if it is possible to have TOP N rows of table, when N is variable.
If I am right, you will need to specify a column which the table would be ordered.
Then you can use something like:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY COLUMN_NAME) TOPCOL
FROM TABLE_NAME
) A
WHERE TOPCOL <= N
If I am not right, you should edit your question, because it is very hard to understand what you meant

SQL Server: UPDATE a table by using ORDER BY

I would like to know if there is a way to use an order by clause when updating a table. I am updating a table and setting a consecutive number, that's why the order of the update is important. Using the following sql statement, I was able to solve it without using a cursor:
DECLARE #Number INT = 0
UPDATE Test
SET #Number = Number = #Number +1
now what I'd like to to do is an order by clause like so:
DECLARE #Number INT = 0
UPDATE Test
SET #Number = Number = #Number +1
ORDER BY Test.Id DESC
I've read: How to update and order by using ms sql The solutions to this question do not solve the ordering problem - they just filter the items on which the update is applied.
Take care,
Martin
No.
Not a documented 100% supported way. There is an approach sometimes used for calculating running totals called "quirky update" that suggests that it might update in order of clustered index if certain conditions are met but as far as I know this relies completely on empirical observation rather than any guarantee.
But what version of SQL Server are you on? If SQL2005+ you might be able to do something with row_number and a CTE (You can update the CTE)
With cte As
(
SELECT id,Number,
ROW_NUMBER() OVER (ORDER BY id DESC) AS RN
FROM Test
)
UPDATE cte SET Number=RN
You can not use ORDER BY as part of the UPDATE statement (you can use in sub-selects that are part of the update).
UPDATE Test
SET Number = rowNumber
FROM Test
INNER JOIN
(SELECT ID, row_number() OVER (ORDER BY ID DESC) as rowNumber
FROM Test) drRowNumbers ON drRowNumbers.ID = Test.ID
Edit
Following solution could have problems with clustered indexes involved as mentioned here. Thanks to Martin for pointing this out.
The answer is kept to educate those (like me) who don't know all side-effects or ins and outs of SQL Server.
Expanding on the answer gaven by Quassnoi in your link, following works
DECLARE #Test TABLE (Number INTEGER, AText VARCHAR(2), ID INTEGER)
DECLARE #Number INT
INSERT INTO #Test VALUES (1, 'A', 1)
INSERT INTO #Test VALUES (2, 'B', 2)
INSERT INTO #Test VALUES (1, 'E', 5)
INSERT INTO #Test VALUES (3, 'C', 3)
INSERT INTO #Test VALUES (2, 'D', 4)
SET #Number = 0
;WITH q AS (
SELECT TOP 1000000 *
FROM #Test
ORDER BY
ID
)
UPDATE q
SET #Number = Number = #Number + 1
The row_number() function would be the best approach to this problem.
UPDATE T
SET T.Number = R.rowNum
FROM Test T
JOIN (
SELECT T2.id,row_number() over (order by T2.Id desc) rowNum from Test T2
) R on T.id=R.id
update based on Ordering by the order of values in a SQL IN() clause
Solution:
DECLARE #counter int
SET #counter = 0
;WITH q AS
(
select * from Products WHERE ID in (SELECT TOP (10) ID FROM Products WHERE ID IN( 3,2,1)
ORDER BY ID DESC)
)
update q set Display= #counter, #counter = #counter + 1
This updates based on descending 3,2,1
Hope helps someone.
I had a similar problem and solved it using ROW_NUMBER() in combination with the OVER keyword. The task was to retrospectively populate a new TicketNo (integer) field in a simple table based on the original CreatedDate, and grouped by ModuleId - so that ticket numbers started at 1 within each Module group and incremented by date. The table already had a TicketID primary key (a GUID).
Here's the SQL:
UPDATE Tickets SET TicketNo=T2.RowNo
FROM Tickets
INNER JOIN
(select TicketID, TicketNo,
ROW_NUMBER() OVER (PARTITION BY ModuleId ORDER BY DateCreated) AS RowNo from Tickets)
AS T2 ON T2.TicketID = Tickets.TicketID
Worked a treat!
I ran into the same problem and was able to resolve it in very powerful way that allows unlimited sorting possibilities.
I created a View using (saving) 2 sort orders (*explanation on how to do so below).
After that I simply applied the update queries to the View created and it worked great.
Here are the 2 queries I used on the view:
1st Query:
Update MyView
Set SortID=0
2nd Query:
DECLARE #sortID int
SET #sortID = 0
UPDATE MyView
SET #sortID = sortID = #sortID + 1
*To be able to save the sorting on the View I put TOP into the SELECT statement. This very useful workaround allows the View results to be returned sorted as set when the View was created when the View is opened. In my case it looked like:
(NOTE: Using this workaround will place an big load on the server if using a large table and it is therefore recommended to include as few fields as possible in the view if working with large tables)
SELECT TOP (600000)
dbo.Items.ID, dbo.Items.Code, dbo.Items.SortID, dbo.Supplier.Date,
dbo.Supplier.Code AS Expr1
FROM dbo.Items INNER JOIN
dbo.Supplier ON dbo.Items.SupplierCode = dbo.Supplier.Code
ORDER BY dbo.Supplier.Date, dbo.Items.ID DESC
Running: SQL Server 2005 on a Windows Server 2003
Additional Keywords: How to Update a SQL column with Ascending or Descending Numbers - Numeric Values / how to set order in SQL update statement / how to save order by in sql view / increment sql update / auto autoincrement sql update / create sql field with ascending numbers
SET #pos := 0;
UPDATE TABLE_NAME SET Roll_No = ( SELECT #pos := #pos + 1 ) ORDER BY First_Name ASC;
In the above example query simply update the student Roll_No column depending on the student Frist_Name column. From 1 to No_of_records in the table. I hope it's clear now.
IF OBJECT_ID('tempdb..#TAB') IS NOT NULL
BEGIN
DROP TABLE #TAB
END
CREATE TABLE #TAB(CH1 INT,CH2 INT,CH3 INT)
DECLARE #CH2 INT = NULL , #CH3 INT=NULL,#SPID INT=NULL,#SQL NVARCHAR(4000)='', #ParmDefinition NVARCHAR(50)= '',
#RET_MESSAGE AS VARCHAR(8000)='',#RET_ERROR INT=0
SET #ParmDefinition='#SPID INT,#CH2 INT OUTPUT,#CH3 INT OUTPUT'
SET #SQL='UPDATE T
SET CH1=#SPID,#CH2= T.CH2,#CH3= T.CH3
FROM #TAB T WITH(ROWLOCK)
INNER JOIN (
SELECT TOP(1) CH1,CH2,CH3
FROM
#TAB WITH(NOLOCK)
WHERE CH1 IS NULL
ORDER BY CH2 DESC) V ON T.CH2= V.CH2 AND T.CH3= V.CH3'
INSERT INTO #TAB
(CH2 ,CH3 )
SELECT 1,2 UNION ALL
SELECT 2,3 UNION ALL
SELECT 3,4
BEGIN TRY
WHILE EXISTS(SELECT TOP 1 1 FROM #TAB WHERE CH1 IS NULL)
BEGIN
EXECUTE #RET_ERROR = sp_executesql #SQL, #ParmDefinition,#SPID =##SPID, #CH2=#CH2 OUTPUT,#CH3=#CH3 OUTPUT;
SELECT * FROM #TAB
SELECT #CH2,#CH3
END
END TRY
BEGIN CATCH
SET #RET_ERROR=ERROR_NUMBER()
SET #RET_MESSAGE = '#ERROR_NUMBER : ' + CAST(ERROR_NUMBER() AS VARCHAR(255)) + '#ERROR_SEVERITY :' + CAST( ERROR_SEVERITY() AS VARCHAR(255))
+ '#ERROR_STATE :' + CAST(ERROR_STATE() AS VARCHAR(255)) + '#ERROR_LINE :' + CAST( ERROR_LINE() AS VARCHAR(255))
+ '#ERROR_MESSAGE :' + ERROR_MESSAGE() ;
SELECT #RET_ERROR,#RET_MESSAGE;
END CATCH

Resources