I am creating a store procedure and in which am I stuck in a problem. I want to query two columns based on condition. If parameter is numeric then query to one column and if it is nonnumeric then query to other column. Following is the procedure.
$
declare #result AS varchar(50)
DECLARE #peopleId AS varchar(50)
if('232332' NOT LIKE '%[^0-9]%')
BEGIN
SET #result='Numeric'
PRINT #result
END
ELSE
BEGIN
set #result='nonNumeric'
print #result
END
select isnull(class.grade,'') as grade,ISNULL(class.room,'') as room,student.prefix as prefix,student.student_id as student_id,(person.first_name+' '+person.last_name) as name,
person.dob as dob,person.people_id as people_id,quit_on,
case when student.student_status='30' then
N'พักการเรียน'
when student.student_status='31' then
N'น.ร.ไปเรียนโครงการฯ'
else ''
end
as quit_reason from school_student student
inner join people_person person on student.person_id=person.id
left join school_classroom_students classStudent on classStudent.student_id=student.id
left join school_classroom class on class.id =classStudent.classroom_id
where student.student_status in('30','31') and student.system_status = 'DC' and student.school_id=#schoolId
AND case
WHEN
#result='nonNumeric' then-- this should execure
person.people_id=#peopleId
else---- this should work
person.first_name+' '+ person.last_name LIKE '%'+#peopleId+'%'
Please help me out on this
Why would use use a separate variable? You can do:
WHEN (person.people_id = try_convert(int, #peopleId) or
try_convert(int, #peopleId) is null and
person.first_name + ' ' + person.last_name LIKE '%' + #peopleId + '%'
)
I question why you are passing a value that is used for both a string and numeric comparison. If I were using a variable, I would do:
declare #personid int;
declare #personname varchar(255);
if #peopleid not like '%[^0-9]%'
set #personname = #peopleid;
else
set #personid = convert(int, #peopleid);
where (personid = #personid or
person.first_name + ' ' + person.last_name LIKE '%' + #personname + '%'
)
The code just seems easier to follow.
Since SQL Server doesn't treat results of CASE expressions as booleans, you have to add an extra comparison. The way to do that is like this:
WHERE 1 = CASE WHEN x THEN 1 WHEN y THEN 0 ELSE 1 END
Conditions which result in rows being included in the result must evaluate to 1, and conditions which don't, must evaluate to something other than 1 (like 0). So the whole CASE expression returns either 0 or 1, and that is compared to 1.
In your code, it would look like this:
AND 1 = case
WHEN
#result='nonNumeric' then case when person.person_id = #peopleId then 1 else 0 end
else when person.first_name+' '+person.last_name LIKE '%'+#peopleId+'%' then 1 else 0 end
end
I added the END.
Just do like that
DECLARE #IsNumeric INT = NULL
DECLARE #IsNotNumeric INT = NULL
DECLARE #peopleId Varchar(50)
SET #peopleId = '123'
IF ISNUMERIC(#peopleId) = 1
BEGIN
SET #IsNumeric = 1
END
ELSE
BEGIN
SET #IsNotNumeric = 1
END
IN WHERE Condition Just Check
AND (#IsNumeric IS NULL OR CONVERT(VARCHAR(500),person.people_id)=#peopleId)
AND (#IsNotNumeric IS NULL OR person.first_name+' '+ person.last_name LIKE '%'+#peopleId+'%')
I'm trying to merge one table into another (we'll call them Stage and Prod) that controls users and their permissions. My end result should be a single Prod table that has combined each userid's permissions from Stage into Prod. The issue I'm having though is that the tables were designed by an outside vendor and contain multiple pieces of information in one comma-delimited column.
Stage might look like below:
Userid | Permissions
----------------------------------------------------------------
1 | schedule,upload,test,download,admin
2 | test,upload
3 | download
Prod:
Userid | Permissions
----------------------------------------------------------------
1 | test,admin,schedule,download,upload
2 | admin
3 | download,upload
When they're merged, the userids should have their permissions from Stage, combined with those in Prod. However, tackling this when the permissions are a comma-delimited string has me at wit's end.
In the final result below, userid 1's permissions remain unchanged because they are the same in Stage as they are in Prod, merely in a different order.
Userid 2 had his Stage permissions added to his Prod since he did not have those permissions yet.
Userid 3 had his Prod permissions unchanged since his Stage permissions are already included.
Result:
Userid | Permissions
----------------------------------------------------------------
1 | test,admin,schedule,download,upload
2 | admin,test,upload
3 | download,upload
Is there any way to do this? Hopefully this makes some sense, but if there's any more info that might help I'm happy to try to provide it. Thank you for any help at all.
Interestingly enough, this was a topic of discussion on a MSSQLTips blog by Aaron Bertrand. Borrowing his code you can create the Numbers table and string splitting/reassembling functions required to make the following work. If you are planning on doing this often and are stuck with the schema you've shown, this is the way to go.
/*Create Test Data
create table StagePermissions (UserID int, [Permissions] nvarchar(max));
create table ProdPermissions (UserID int, [Permissions] nvarchar(max));
insert StagePermissions values
(1,'schedule,upload,test,download,admin'),
(2,'test,upload'),
(3,'download')
insert ProdPermissions values
(1,'test,admin,schedule,download,upload'),
(2,'admin'),
(3,'download,upload')
*/
select sp.UserID, dbo.ReassembleString(sp.Permissions+','+pp.Permissions,',',N'OriginalOrder') MergedPermissions
from StagePermissions sp
join ProdPermissions pp on pp.UserID=sp.UserID
Taking Steve's test data, but adding:
create table BothPermissions (UserID int, [Permissions] nvarchar(max));
This code will work with a fixed number of possible permissions.
DECLARE #XPermissions TABLE (
UserID int
,XSchedule BIT
,XUpload BIT
,XTest BIT
,XDownload BIT
,XAdmin BIT
)
INSERT INTO #XPermissions
SELECT
ISNULL(sp.UserID,pp.UserID),
CHARINDEX('schedule',sp.[Permissions]) + CHARINDEX('schedule',pp.[Permissions]),
CHARINDEX('upload',sp.[Permissions]) + CHARINDEX('upload',pp.[Permissions]),
CHARINDEX('test',sp.[Permissions]) + CHARINDEX('test',pp.[Permissions]),
CHARINDEX('download',sp.[Permissions]) + CHARINDEX('download',pp.[Permissions]),
CHARINDEX('admin',sp.[Permissions]) + CHARINDEX('admin',pp.[Permissions])
FROM StagePermissions sp
FULL JOIN ProdPermissions pp
ON sp.UserID = pp.UserID
INSERT INTO BothPermissions
SELECT
UserID,
CASE XSchedule WHEN 0 THEN '' ELSE 'schedule ' END +
CASE XUpload WHEN 0 THEN '' ELSE 'upload ' END +
CASE XTest WHEN 0 THEN '' ELSE 'test ' END +
CASE XDownload WHEN 0 THEN '' ELSE 'download ' END +
CASE XAdmin WHEN 0 THEN '' ELSE 'admin' END
FROM #XPermissions
UPDATE BothPermissions
SET [Permissions] = REPLACE(RTRIM([Permissions]),' ',', ')
Now, I was further curious about Steve's answer. I think it is the most robust solution here. However, I wondered how it would perform with a large dataset. I still don't know the answer because I haven't set up the tools necessary to use it. But here's a query that includes some random number generation to populate 10,000 records of each:
SELECT GETDATE()
DECLARE #StagePerms TABLE (
UserID INT IDENTITY
,Perms NVARCHAR(MAX)
)
DECLARE #ProdPerms TABLE (
UserID INT IDENTITY
,Perms NVARCHAR(MAX)
)
DECLARE #Counter INT = 0
DECLARE #XString NVARCHAR(MAX)
WHILE #Counter < 10000
BEGIN
SET #Counter += 1
SET #XString = REPLACE(RTRIM(
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'test ' END +
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'admin ' END +
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'schedule ' END +
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'download ' END +
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'upload ' END)
,' ',', ')
INSERT INTO #StagePerms SELECT #XString
SET #XString = REPLACE(RTRIM(
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'test ' END +
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'admin ' END +
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'schedule ' END +
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'download ' END +
CASE ROUND(RAND()-.2,0) WHEN 0 THEN '' ELSE 'upload ' END)
,' ',', ')
INSERT INTO #ProdPerms SELECT #XString
END
SELECT GETDATE()
DECLARE #BothPerms TABLE (
UserID INT
,Perms NVARCHAR(MAX)
)
DECLARE #XPerms TABLE (
UserID int
,XSchedule BIT
,XUpload BIT
,XTest BIT
,XDownload BIT
,XAdmin BIT
)
INSERT INTO #XPerms
SELECT
ISNULL(sp.UserID,pp.UserID),
CHARINDEX('schedule',sp.Perms) + CHARINDEX('schedule',pp.Perms),
CHARINDEX('upload',sp.Perms) + CHARINDEX('upload',pp.Perms),
CHARINDEX('test',sp.Perms) + CHARINDEX('test',pp.Perms),
CHARINDEX('download',sp.Perms) + CHARINDEX('download',pp.Perms),
CHARINDEX('admin',sp.Perms) + CHARINDEX('admin',pp.Perms)
FROM #StagePerms sp
FULL JOIN #ProdPerms pp
ON sp.UserID = pp.UserID
INSERT INTO #BothPerms
SELECT
UserID,
CASE XTest WHEN 0 THEN '' ELSE 'test ' END +
CASE XAdmin WHEN 0 THEN '' ELSE 'admin ' END +
CASE XSchedule WHEN 0 THEN '' ELSE 'schedule ' END +
CASE XDownload WHEN 0 THEN '' ELSE 'download ' END +
CASE XUpload WHEN 0 THEN '' ELSE 'upload ' END
FROM #XPerms
UPDATE #BothPerms
SET Perms = REPLACE(RTRIM(Perms),' ',', ')
SELECT * FROM #BothPerms
SELECT GETDATE()
The random number generation took less than a second; the rest took about 31 seconds. Steve, I'd be interested to see a comparison. Doesn't matter, obviously, if the data doesn't allow for my solution. And I'm sure there's a sweet spot somewhere.
Please make use of the below query. Its working fine in SQL Server 2012.
DECLARE #Stage TABLE (Userid int, Permission Varchar (8000))
DECLARE #Prod TABLE (Userid int, Permission Varchar (8000))
DECLARE #temp TABLE (Userid int, Permission Varchar (8000))
INSERT #Stage
(Userid,Permission)
VALUES
(1,'schedule,upload,test,download,admin'),
(2,'test,upload'),
(3,'download')
INSERT #Prod
(Userid,Permission)
VALUES
(1,'test,admin,schedule,download,upload'),
(2,'admin'),
(3,'download,upload')
-- Execution Part
INSERT INTO #temp
(Userid,Permission)
(
SELECT A.Userid AS Userid,Split.a.value('.', 'VARCHAR(100)') AS Permission FROM
(SELECT Userid,CAST ('<M>' + REPLACE(Permission, ',', '</M><M>') + '</M>' AS XML) AS Permission FROM #Stage A) AS A
CROSS APPLY Permission.nodes ('/M') AS Split(a)
UNION
SELECT A.Userid AS Userid,Split.a.value('.', 'VARCHAR(100)') AS Permission FROM
(SELECT Userid,CAST ('<M>' + REPLACE(Permission, ',', '</M><M>') + '</M>' AS XML) AS Permission FROM #Prod A) AS A
CROSS APPLY Permission.nodes ('/M') AS Split(a)
)
SELECT Userid, Permission =
STUFF((SELECT ', ' + Permission
FROM #temp b
WHERE b.Userid = a.Userid
FOR XML PATH('')), 1, 2, '')
FROM #temp a
GROUP BY Userid
OUTPUT
Userid Permission
1 admin, download, schedule, test, upload
2 admin, test, upload
3 download, upload
You can also use direct support of string splitting introduced in SQL Serv 2016 (in case you started using this engine version already of course :) )
STRING_SPLIT returns single column table...
I want this procedure change the table name when I execute it.
The table name that I want to change is Recargas_#mes
There is some way to do that?
#MES DATETIME
AS
BEGIN
SELECT CUENTA, SUM(COSTO_REC) COSTO_REC
INTO E09040_DEV.BI_PRO_COSTO_RECARGAS
FROM (
SELECT a.*,(CASE
WHEN COD_AJUSTE IN ('ELEC_TEXT','TFREPPVV_C') THEN (A.VALOR)*(R.COSTO) ELSE 0 END)
FROM Recargas_#MES AS A, BI_PRO_LISTA_COSTOS_RECARGAS AS R
WHERE R.ANO_MES = #MES
) D
GROUP BY CUENTA
END
Sample code:
-- Declare variables
DECLARE #MES DATETIME;
DECLARE #TSQL NVARCHAR(MAX);
-- Set the variable to valid statement
SET #TSQL = N'
SELECT CUENTA, SUM(COSTO_REC) AS COSTO_REC
INTO E09040_DEV.BI_PRO_COSTO_RECARGAS
FROM (
SELECT A.*,
(CASE
WHEN COD_AJUSTE IN (''ELEC_TEXT'',''TFREPPVV_C'') THEN
(A.VALOR)*(R.COSTO)
ELSE 0
END)
FROM
Recargas_' + REPLACE(CONVERT(CHAR(10), #MES, 101), '/', '') + ' AS A,
BI_PRO_LISTA_COSTOS_RECARGAS AS R
WHERE R.ANO_MES = ' + CONVERT(CHAR(10), #MES, 101) + '
) D
GROUP BY CUENTA'
-- Execute the statement
EXECUTE (#SQL)
Some things to note:
1 - I assume the table name has some type of extension that is a date? I used MM/DD/YYYY and removed the slashes as a format for the suffix.
2 - The WHERE clause will only work if you are not using the time part of the variable.
For instance, 03/15/2016 00:00:00 would be date without time entry. If not, you will have to use >= and < to grab all hours for a particular day.
3 - You are creating a table on the fly with this code. On the second execution, you will get a error unless you drop the table.
4 - You are not using the ON clause when joining table A to table R. To be ANSI compliant, move the WHERE clause to a ON clause.
5 - The actual calculation created by the CASE statement is not give a column name.
Issues 3 to 5 have to be solved on your end since I do not have the detailed business requirements.
Have Fun.
It should work using dynamic SQL to allow putting a dynamic table name:
DECLARE #SQL NVARCHAR(MAX) = N'
SELECT CUENTA, SUM(COSTO_REC) COSTO_REC
INTO E09040_DEV.BI_PRO_COSTO_RECARGAS
FROM (
SELECT a.*,(CASE
WHEN COD_AJUSTE IN (''ELEC_TEXT'',''TFREPPVV_C'') THEN (A.VALOR)*(R.COSTO) ELSE 0 END)
FROM Recargas_' + #MES + ' AS A, BI_PRO_LISTA_COSTOS_RECARGAS AS R
WHERE R.ANO_MES = ' + CAST(#MES AS VARCHAR(32)) + '
) D
GROUP BY CUENTA'
EXECUTE (#SQL)
I want to check if all given word fragments exist in any order in a given text.
The fragments are supplied by a web application user in a single string separated by spaces like 'abc xyz kj'. They exist in 'mn kj qabc pc xyzw' but do not exist in 'mn kj qabc pc xyw'.
I wrote the following function which works but it looks quite convoluted so I must be doing it wrong. Any ideas on different approaches or how to make it perform?
BTW the database is read only for me so I can't full-text index it and the owners will not do it.
create function dbo.tem_fragmentos(
#texto varchar(max),
#fragmentos varchar(max)
)
returns bit as
begin
declare
#inicio integer = 1,
#fim integer,
#fragmento varchar(max);
set #fragmentos = ltrim(rtrim(#fragmentos));
while charindex(' ', #fragmentos) > 0
set #fragmentos = replace(#fragmentos, ' ', ' ');
while #inicio <= len(#fragmentos) begin
set #fim = charindex(' ', #fragmentos, #inicio + 1);
if #fim = 0 set #fim = len(#fragmentos) + 1;
set #fragmento = substring(#fragmentos, #inicio, #fim - #inicio);
if charindex(#fragmento, #texto) = 0 return 0;
set #inicio = #fim + 1;
end -- while
return 1;
end;
select dbo.tem_fragmentos('clodoaldo pinto neto', ' clo cl nto pinto');
This is how I would do it. Not sure it's any less convoluted...
Create Function dbo.tem_fragmentos
(
#texto varchar(max),
#fragmentos varchar(max)
)
Returns Bit As
Begin
Declare #table Table (fragmentos Varchar(Max))
Set #fragmentos = Ltrim(Rtrim(#fragmentos))
While #fragmentos <> ''
Begin
Insert #table (fragmentos)
Select Left(#fragmentos,Charindex(' ',#fragmentos+' ')-1)
Set #fragmentos = Ltrim(Rtrim(Right(#fragmentos,Len(#fragmentos)-(Charindex(' ',#fragmentos+' ')-1))));
end
If Exists (Select 1
From #table t
Where #texto Not Like '%' + fragmentos + '%')
Begin
Return 0;
End
Return 1;
End;
Select dbo.tem_fragmentos('clodoaldo pinto neto', ' clo cl nto pinto');
I'm assuming your text exists in a db table, else you wouldn't have the db server doing the work. So, why not have your app break the string on spaces and build dynamic SQL like:
select *
from MyTable
where charindex('abc', MyColumn) > 0
and charindex('xyz', MyColumn) > 0
and charindex('kj', MyColumn) > 0
Update:
If you don't want to use dynamic SQL, I would split the input into words in my application, and then pass the list of words in to the query using a table valued parameter (TVP). Then it is a simple left join to determine whether they all match or not.
Sounds like a wildcarded LIKE search should work for you:
declare #texto varchar(max) = 'mn kj q abc pc xyzw',
#fragmentos varchar(max) = 'abc xyz kj'
/*
yes = 'mn kj qabc pc xyzw'
no = 'mn kj qabc pc xyw'
*/
--use your own number table
declare #number table (n int identity(1,1) primary key clustered, x char(1) null);
insert into #number(x)
select top 1000 null from master..spt_values
select [IsMatch] = min(case when #texto like '%'+substring(#fragmentos, n, charindex(' ', #fragmentos + ' ', n) - n)+'%' then 1 else 0 end)
from #number
where n <= datalength(#fragmentos)+1 and
substring(' ' + #fragmentos, N, 1) = ' ';
Does anyone have in their back pocket a function that can achieve this?
Found this here :-
create function ProperCase(#Text as varchar(8000))
returns varchar(8000)
as
begin
declare #Reset bit;
declare #Ret varchar(8000);
declare #i int;
declare #c char(1);
select #Reset = 1, #i=1, #Ret = '';
while (#i <= len(#Text))
select #c= substring(#Text,#i,1),
#Ret = #Ret + case when #Reset=1 then UPPER(#c) else LOWER(#c) end,
#Reset = case when #c like '[a-zA-Z]' then 0 else 1 end,
#i = #i +1
return #Ret
end
Results from this:-
select dbo.propercase('ALL UPPERCASE'); -- All Uppercase
select dbo.propercase('MiXeD CaSe'); -- Mixed Case
select dbo.propercase('lower case'); -- Lower Case
select dbo.propercase('names with apostrophe - mr o''reilly '); -- Names With Apostrophe - Mr O'Reilly
select dbo.propercase('names with hyphen - mary two-barrels '); -- Names With Hyphen - Mary Two-Barrels
I'd do this outside of TSQL, in the calling code tbh.
e.g. if you're using .NET, it's just a case of using TextInfo.ToTitleCase.
That way, you leave your formatting code outside of TSQL (standard "let the caller decide how to use/format the data" approach).
This kind of function is better done on the application side, as it will perform relatively poorly in SQL.
With SQL-Server 2005 and above you could write a CLR function that does that and call it from your SQL. Here is an article on how to do this.
If you really want to do this in T-SQL and without a loop, see Tony Rogerson's article "Turning stuff into "Camel Case" without loops"
I haven't tried it... that's what client code it for :-)
No cursors, no while loops, no (inline) sub-queries
-- ===== IF YOU DON'T HAVE A NUMBERS TABLE =================
--CREATE TABLE Numbers (
-- Num INT NOT NULL PRIMARY KEY CLUSTERED WITH(FILLFACTOR = 100)
--)
--INSERT INTO Numbers
--SELECT TOP(11000)
-- ROW_NUMBER() OVER (ORDER BY (SELECT 1))
--FROM master.sys.all_columns a
-- CROSS JOIN master.sys.all_columns b
DECLARE #text VARCHAR(8000) = 'my text to make title-case';
DECLARE #result VARCHAR(8000);
SET #result = UPPER(LEFT(#text, 1));
SELECT
#result +=
CASE
WHEN SUBSTRING(#text, Num - 1, 1) IN (' ', '-') THEN UPPER(SUBSTRING(#text, Num, 1))
ELSE SUBSTRING(#text, Num, 1)
END
FROM Numbers
WHERE Num > 1 AND Num <= LEN(#text);
PRINT #result;
Will any given row only contain a firstname or a lastname that you wish to convert or will it contain full names separated by spaces? Also, are there any other rules you wish to what characters it should "upper" or lower"?
If you can guarantee that it's only first and last names and you aren't dealing with any specialized capitalization such as after an apostrophe, might this do what you're looking for?
SELECT -- Initial of First Name
UPPER(LEFT(FullName, 1))
-- Rest of First Name
+ SUBSTRING(LOWER(FullName), 2, CHARINDEX(' ', FullName, 0) - 2)
-- Space between names
+ ' '
-- Inital of last name
+ UPPER(SUBSTRING(FullName, CHARINDEX(' ', FullName, 0) + 1, 1))
-- Rest of last name
+ SUBSTRING(LOWER(FullName), CHARINDEX(' ', FullName, 0) + 2, LEN(FullName) - CHARINDEX(' ', FullName, 0) + 2)
FROM Employee