Prevent leading zero in front of the letter grade? - sql

I use CASE statement in SQL to check the value for Grade column. If user entered 7 for example I stick leading 0 in front so value will be saved as 07. Everything worked fine until we added K grade as an option in our app. Now if user enter K value will be saved as 0K. Here is example of my SQL Case statement:
CASE
WHEN LEN(LTRIM(RTRIM(GRADE))) = 0 THEN NULL
ELSE RIGHT('00'+ISNULL(LTRIM(RTRIM(GRADE)),''),2)
END
Code above is used in INSERT SELECT statement. I'm wondering if I can prevent leading zero when user enter K grade? Is that doable with modifying existing CASE statement? I have tried adding this:
WHEN GRADE = 'K' THEN 'K'
But this did not fix the problem, my INSERT statement failed. If anyone can help or knows better solution please let me know. Thank you!

This is the way I would do it -
IF EXISTS(SELECT TOP 1 1 FROM sys.tables WHERE name = 'tb_Test')
BEGIN
DROP TABLE dbo.tb_Test
END
CREATE TABLE dbo.tb_Test (ID INT IDENTITY(1,1) PRIMARY KEY, Grade NVARCHAR(10))
INSERT dbo.tb_Test (Grade)
VALUES ('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9'),('10'),('K')
SELECT CASE WHEN LEN(Grade) = 1 AND ISNUMERIC(Grade) = 1 THEN '0'+Grade
WHEN ISNUMERIC(Grade) = 1 THEN Grade
WHEN ISNUMERIC(Grade) = 0 THEN Grade END
FROM dbo.tb_Test

Here is one way to do it:
Create and populate sample table (Please save us this step in your future questions)
DECLARE #T AS TABLE
(
GRADE varchar(3)
)
INSERT INTO #T VALUES
('7'), (''),('03'),('001'),('K'), (NULL)
The query:
SELECT CASE WHEN LEN(LTRIM(RTRIM(GRADE))) = 0 THEN NULL
WHEN GRADE NOT LIKE '%[^0-9]%' THEN RIGHT('00' + GRADE, 3)
ELSE GRADE
END As Res
FROM #T
Results:
Res
007
NULL
003
001
K
NULL
See a live demo on rextester.

Related

SQL Script to find Count the number of :username Occurrences of a String

I have a table that stores information whenever user make changes to the DB. I want to extract how many times the user make changes on the date on the application. The info is normally stored for each user in one row for example :
2019-06-15randomname1:YES I DID IT 2019-06-14randomname2:HHHHHHH JJJJJJ 2019-06-14Urandomnamexxxxxx: COMMENT OF PEOPLE
What I want is to search :username to detect how many times the user has changed. In this instance. The answer suppose to be 3. How can I do it
DECLARE #logEntry VARCHAR(4000);
SET #logEntry = ':' + (SELECT PERSON_NAME FROM P_PERSON WHERE PERSON = logged_person)
SELECT
id
,value
,COUNT = (LEN(value) - LEN(REPLACE(value, #logEntry , '')))/LEN(#logEntry)
FROM table
Will I use regular expression because for this particular example the answer will be 3 since we have 3.
I have decided to use :username I am having problem with Subquery returned more than 1 value :
If I understand, you want to count the occurrence of a date in a string
DECLARE #D VARCHAR(10) = '2019-01-01';
SELECT *, LEN(V) - (LEN(REPLACE(V, #D, '')) * 10) Occurrence
FROM (VALUES('A2019-01-01B2019-01-01C2019-01-01D2019-01-01E2019-01-01F2019-01-01'))T(V);
Returns:
+--------------------------------------------------------------------+------------+
| V | Occurrence |
+--------------------------------------------------------------------+------------+
| A2019-01-01B2019-01-01C2019-01-01D2019-01-01E2019-01-01F2019-01-01 | 6 |
+--------------------------------------------------------------------+------------+
Note that this will works only when the string doesn't contains a white spaces.
If you have a white spaces, then you need to remove them first as
DECLARE #D VARCHAR(10) = '2019-01-01';
SELECT *, LEN(REPLACE(V, ' ', '')) - (LEN(REPLACE(REPLACE(V, ' ', ''), #D, '')) * 10) Occurrence
FROM (VALUES('A 2019-01-01 B 2019-01-01 C 2019-01-01 D 2019-01-01 E 2019-01-01 F 2019-01-01'))T(V);
You just changed your question, to search by a user name, but since the ':' is fixed, and if you have 2016+ version you can do as
DECLARE #D VARCHAR(10) = 'UserName1';
SELECT *,
(SELECT COUNT(1) FROM STRING_SPLIT(V, ':') WHERE Value LIKE CONCAT('%', #D, '%'))
FROM (VALUES
('2019-06-15UserName1:YES I DID IT 2019-06-14UserName2:HHHHHHH JJJJJJ 2019-06-14UserName1: COMMENT OF PEOPLE')
) T(V);
Finally, I'll recommend to re-think of that design, which is the real issue here, and read more about normalization.
UPDATE:
Here is how to count the user name with joining the two tables
SELECT *,
(
SELECT COUNT(1)
FROM STRING_SPLIT(Col, ':')
WHERE Value LIKE CONCAT('%', UserName)
) Cnt
FROM Users U JOIN Data D
ON D.Col LIKE CONCAT('%', U.UserName, '%');
Returns:
+----------+----------------------------------------------+-----+
| UserName | Col | Cnt |
+----------+----------------------------------------------+-----+
| User1 | 2019-01-01User1:YES 2019-01-02User2:No | 1 |
| User2 | 2019-01-01User1:YES 2019-01-02User2:No | 1 |
| User1 | 2019-01-01User1:YES I 2019-01-02User1:No Way | 2 |
+----------+----------------------------------------------+-----+
See how it's working on live demo
First, you have a lousy data model and processing. You should not be just adding substrings to a string. You should be adding new rows to a table. And, you should not be encoding information in a string. You should be using columns for that.
My strongest suggestion is that you fix your data model and processing.
That said, you might be stuck with this situation. THe simplest solution is just to look for
SELECT id, value,
(LEN(REPLACE(value, 'XXXXXXXXXXXXX:', 'XXXXXXXXXXXXX:1') -
LEN(value)
) as Num_Times
FROM Table;
Of course, this assumes that 'XXXXXXXXXXXXX:' doesn't actually occur in the message. If that is a possibility, see my original comment on the data structure.
The following will do as you ask, but you seriously need to reconsider how you store your data. What if instead of someone commenting "I did it", they entered "I did it on 2019-01-01"?
-- DateCount
-- Return number of occurances of ####-##-## where # is a digit
create function dbo.DateCount(#s nvarchar(max))
returns int as
begin
declare #k int = 0 -- #k holds the count so far
declare #i int = 1 -- index into string, start at first character
while #i < len(#s)-9 -- keep checking until we get to the end
begin
if substring(#s,#i,10) like '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
set #k = #k + 1 -- increment count if these 10 characters match
set #i = #i + 1 -- check the next character
end
return #k -- return the count
end
go
select dbo.DateCount( '2019-06-15randomname1:YES I DID IT 2019-06-14random'
+ 'name2:HHHHHHH JJJJJJ 2019-06-14Urandomnamexxxxxx: '
+ 'COMMENT OF PEOPLE' )
-- Result is 3
If you're keen on using a set-based solution instead of a while loop, you can try this:
create function dbo.DateCount(#s nvarchar(max))
returns int as
begin
declare #k int;
with A as ( select 1 as I
union all
select I+1 as I from A where I<=len(#s)-9 )
select #k=count(*) from A
where substring(#S,I,10) like '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
option (maxrecursion 0)
return #k
end
But, in my performance tests, I find that the set-based solution takes 50% longer.

Take a value from an SQL table cell and insert it in another cell

I have an old SQL database (Microsoft's SQL Server) with thousands of rows that contains data as follows:
ID urlString
1 page.aspx?pageID=34
2 page.aspx?pageID=163
3 page.aspx
4 page.aspx?pageID=23
I've added a new column (pageID) to the database. I want to create an UPDATE query to copy the pageID from the URLstring and insert it in the new column (pageID) as follows. If there is no pageID I want to add 0. How can I accomplish that?
ID URLstring pageID
1 page.aspx?pageID=34 34
2 page.aspx?pageID=163 163
3 page.aspx 0
4 page.aspx?pageID=23 23
UPDATE YourTable
SET pageID=
SUBSTRING(urlString,CHARINDEX('=', urlString)+1,CHARINDEX('=', urlString))
To have the 0 value
UPDATE YourTable
SET pageID=
CASE
WHEN CHARINDEX('=', urlString) > 0 THEN
SUBSTRING(urlString,CHARINDEX('=', urlString)+1,CHARINDEX('=', urlString))
ELSE 0
END
please try the following
SELECT urlString, cast(REVERSE(SUBSTRING(REVERSE(urlString),0,CHARINDEX('=',REVERSE(urlString)))) as smallint) AS [pageID]
Hope this helps.
Well, whichever SQL language variant the database uses will make a difference here. (i.e. SQLplus, NOSQL, etc.) However, it shouldn't be toooooooooo complicated. Assuming that both columns are INTs, you could probably just do something like so
UPDATE table_name
SET pageID = URLstring;
Here is some other sources for additional information.
A similar stack overflow question:
Copy data from one column to other column (which is in a different table)
This is a TutorialsPoint webpage on the matter: http://www.tutorialspoint.com/sql/sql-update-query.htm (TutorialsPoint is usually one of my first stops for any programming-related knowledge, it's quite a valuable website.)
This is a w3schools webpage on the matter: http://www.w3schools.com/sql/sql_update.asp
Hope you get everything figured out!
You can achieve this by the PARSENAME using the following UPDATE query:
UPDATE TestTable
SET pageID = CASE WHEN ISNUMERIC(PARSENAME(REPLACE(URLstring, '=', '.'), 1)) = 1 THEN PARSENAME(REPLACE(URLstring, '=', '.'), 1)
ELSE 0 END
Sample execution with the given sample data
DECLARE #TestTable TABLE (ID INT, URLstring VARCHAR (200), PageID INT);
INSERT INTO #TestTable (ID, URLstring, pageID) VALUES
(1, 'page.aspx?pageID=34' , NULL ),
(2, 'page.aspx?pageID=163' , NULL ),
(3, 'page.aspx' , NULL ),
(4, 'page.aspx?pageID=23' , NULL );
UPDATE #TestTable
SET pageID = CASE WHEN ISNUMERIC(PARSENAME(REPLACE(URLstring, '=', '.'), 1)) = 1 THEN PARSENAME(REPLACE(URLstring, '=', '.'), 1)
ELSE 0 END
So the SELECT * FROM #TestTable will result as:
ID URLstring PageID
------------------------------------
1 page.aspx?pageID=34 34
2 page.aspx?pageID=163 163
3 page.aspx 0
4 page.aspx?pageID=23 23
I try it:
update yourtable
set pageID=
case when
substring(URLstring, charindex('=', URLstring) +1, len(URLstring) - charindex('=', URLstring) )
=URLstring then '0'
else
substring(URLstring, charindex('=', URLstring) +1, len(URLstring) - charindex('=', URLstring) )
end
Using CASE, PATINDEX and SUBSTRING it's fairly easy to extract the page id from the url.
Create and populate sample table (Please save us this step in your future questions)
DECLARE #T As TABLE
(
Id int IDENTITY(1,1),
URLString varchar (40),
PageId int
)
INSERT INTO #T (URLString) VALUES
('page.aspx?blabla=yadayada&pageID=34'),
('page.aspx?pageID=163'),
('page.aspx'),
('page.aspx?pageID=23')
Update statement:
UPDATE #T
SET PageId = CAST(
CASE WHEN PATINDEX('%pageID=%', URLString) > 0 THEN
SUBSTRING(URLString, PATINDEX('%pageID=%', URLString) + 7, LEN(URLString))
ELSE
'0'
END
As int)
Verification:
SELECT Id, URLString, PageId
FROM #T
Results:
Id URLString PageId
----------- ---------------------------------------- -----------
1 page.aspx?blabla=yadayada&pageID=34 34
2 page.aspx?pageID=163 163
3 page.aspx 0
4 page.aspx?pageID=23 23

How to filter this in the where clause

So I have a table with 5 different codes and a bunch of amounts. I want to keep codes 1 through 4 no matter what the amount is. However, I want to remove records where the code is 5 and the amount = 0, but I want to keep code 5 when the amount <> 0. How do I go about this?
As per HoneyBadger comment Try like this,
DECLARE #MyTable TABLE (
Code INT
,Amount INT
)
Insert into #MyTable
Values (1,100)
,(2,200)
,(3,300)
,(4,400)
,(5,500)
,(5,0)
SELECT *
FROM #MyTable
WHERE code <> 5
OR amount <> 0

Getting next matching string value from a SQL table

My SQL table have a column name AddressName . I am storing customer address name in that column. And I want to store them in the following format, suppose when I inserted first time a value in that column it stored 'Utpal' in that field next time before inserting any value I want to fetch the next string value to be inserted as 'Utpal2' and will insert that value in the table.Similarly in the next insertion the value should be 'Utpal3'.How to do this task using SQL query please help.
Thanks and Regards
Utpal Maity
Something like this would help you. Just an example:
TABLE SCHEMA:
create table person
(
id int identity(1,1),
address varchar(50)
)
GO
SCRIPT: (partial)
--insert second record
insert into person
select top 1 left(address, case patindex('%[0-9]%', address) when 0 then len(address) else patindex('%[0-9]%', address) - 1 end) +
convert(varchar(10),convert(numeric(10,0),Coalesce(NULLIF(right(address, case patindex('%[0-9]%', address)
when 0 then 0
else len(address) - patindex('%[0-9]%', address) + 1
end
)
,'')
,0)
) + 1
)from person
order by id desc
GO
Check out SqlFiddle:

Finding strings with duplicate letters inside

Can somebody help me with this little task? What I need is a stored procedure that can find duplicate letters (in a row) in a string from a table "a" and after that make a new table "b" with just the id of the string that has a duplicate letter.
Something like this:
Table A
ID Name
1 Matt
2 Daave
3 Toom
4 Mike
5 Eddie
And from that table I can see that Daave, Toom, Eddie have duplicate letters in a row and I would like to make a new table and list their ID's only. Something like:
Table B
ID
2
3
5
Only 2,3,5 because that is the ID of the string that has duplicate letters in their names.
I hope this is understandable and would be very grateful for any help.
In your answer with stored procedure, you have 2 mistakes, one is missing space between column name and LIKE clause, second is missing single quotes around search parameter.
I first create user-defined scalar function which return 1 if string contains duplicate letters:
EDITED
CREATE FUNCTION FindDuplicateLetters
(
#String NVARCHAR(50)
)
RETURNS BIT
AS
BEGIN
DECLARE #Result BIT = 0
DECLARE #Counter INT = 1
WHILE (#Counter <= LEN(#String) - 1)
BEGIN
IF(ASCII((SELECT SUBSTRING(#String, #Counter, 1))) = ASCII((SELECT SUBSTRING(#String, #Counter + 1, 1))))
BEGIN
SET #Result = 1
BREAK
END
SET #Counter = #Counter + 1
END
RETURN #Result
END
GO
After function was created, just call it from simple SELECT query like following:
SELECT
*
FROM
(SELECT
*,
dbo.FindDuplicateLetters(ColumnName) AS Duplicates
FROM TableName) AS a
WHERE a.Duplicates = 1
With this combination, you will get just rows that has duplicate letters.
In any version of SQL, you can do this with a brute force approach:
select *
from t
where t.name like '%aa%' or
t.name like '%bb%' or
. . .
t.name like '%zz%'
If you have a case sensitive collation, then use:
where lower(t.name) like '%aa%' or
. . .
Here's one way.
First create a table of numbers
CREATE TABLE dbo.Numbers
(
number INT PRIMARY KEY
);
INSERT INTO dbo.Numbers
SELECT number
FROM master..spt_values
WHERE type = 'P'
AND number > 0;
Then with that in place you can use
SELECT *
FROM TableA
WHERE EXISTS (SELECT *
FROM dbo.Numbers
WHERE number < LEN(Name)
AND SUBSTRING(Name, number, 1) = SUBSTRING(Name, number + 1, 1))
Though this is an old post it's worth posting a solution that will be faster than a brute force approach or one that uses a scalar udf (which generally drag down performance). Using NGrams8K this is rather simple.
--sample data
declare #table table (id int identity primary key, [name] varchar(20));
insert #table([name]) values ('Mattaa'),('Daave'),('Toom'),('Mike'),('Eddie');
-- solution #1
select id
from #table
cross apply dbo.NGrams8k([name],1)
where charindex(replicate(token,2), [name]) > 0
group by id;
-- solution #2 (SQL 2012+ solution using LAG)
select id
from
(
select id, token, prevToken = lag(token,1) over (partition by id order by position)
from #table
cross apply dbo.NGrams8k([name],1)
) prep
where token = prevToken
group by id; -- optional id you want to remove possible duplicates.
another burte force way:
select *
from t
where t.name ~ '(.)\1';