I have a Nvarchar column which contains data related to different data types such as Numerics, Dates and so on.
I wanted to find out number of cells with Numeric data type and number of cells with Date data type.
Thanks
It looks like you just want IsDate() and IsNumeric():
SELECT Sum(CASE WHEN IsDate(DataColumn) = 1 THEN 1 ELSE 0 END) AS Dates,
Sum(CASE WHEN IsNumeric(DataColumn) = 1 AND IsDate(DataColumn) = 0 THEN 1 ELSE 0 END) AS Numbers
FROM YourTable
This is assuming that you have a single date format, and that that format is supported by SQL Server's dateformat option. To define the correct format, you would use SET DateFormat. See MSDN Documentation on IsDate.
You mention that '20041201' could be a valid date, which is why I added the extra IsDate() check in the numeric count (I assume you wouldn't want to count it as both a date and a number).
First at all you should split you column (create split function which will transform delimited string into column) . You can find a lot of examples in Internet. I have got one here . So here is an example:
declare #text nvarchar(max)='1211221,ssssss,1212,2010-02-01,20100201'
DECLARE #delimiter char(1)=','
;WITH Pieces(pn, start, stop) AS (
SELECT CASt(1 as Int), CAST(1 as integer), CHARINDEX(#delimiter,#text)
UNION ALL
SELECT pn + 1, CAST(stop + 1 as integer), CHARINDEX(#delimiter,#text, stop + 1)
FROM Pieces
WHERE stop > 0
), T as(
SELECT pn,
SUBSTRING(#text, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces)
SELECT
SUM(CASE WHEN ISNUMERIC(T.s)=1 AND ISDATE(T.s)=0 THEN 1 ELSE 0 END) NumericCount,
SUM(CASE WHEN ISDATE(T.s)=0 THEN 0 ELSE 1 END)DateCount
FROM T
You will have 2 dates and 2 numeric counts
Related
I have a list of strings like so:
M308_7
M308_8
M308_9
M308_10
and want to grab the MAX number from the last digits after the "_" of the string and increment this number by one (so the number it should return is 11)
I read on other posts to convert the last digits to integers as 9 is higher than 10 in alphabetical and that was the reason for it returning _9 as the MAX.
I have done this but still the value being returned is 9 when it should be 10
See below what I have so far..
select
#BomNo = MAX(case when CHARINDEX('_',HeaderNo.No_)>0 then
CAST(SUBSTRING(HeaderNo.No_, 6, len(CHARINDEX('_',HeaderNo.No_))) AS INT)else 0 end)
--MAX(case when CHARINDEX('_',NavBomHeader.No_)>0 then CAST(SUBSTRING(HeaderNo.No_,CHARINDEX('_',HeaderNo.No_)+1,len(CHARINDEX('_',HeaderNo.No_))) AS INT) else 0 end)+1
from nameoftable as HeaderNo
where SUBSTRING(HeaderNo.No_, 1, case when CHARINDEX('_',HeaderNo.No_)>0 then CHARINDEX('_',HeaderNo.No_)-1 else len(HeaderNo.No_) end) ='M308'
Another implementation.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (id INT IDENTITY PRIMARY KEY, No_ VARCHAR(30));
INSERT INTO #tbl (No_) VALUES
('M308_7'),
('M308_8'),
('M308_9'),
('M308_10');
-- DDL and sample data population, end
SELECT NextNo = MAX(TRY_CAST(RIGHT(No_, LEN(No_) - pos) AS INT)) + 1
FROM #tbl
CROSS APPLY (SELECT CHARINDEX('_', No_)) AS t(pos);
Output
+--------+
| NextNo |
+--------+
| 11 |
+--------+
You probably have a design flaw in your database. I suspect that you should be using an identity column.
In any case, the answer to your question is logic like this:
select concat('M308_', max(try_convert(int, stuff(no_, 1, charindex('_', no_), ''))) + 1)
from (values ('M308_7'), ('M308_8'), ('M308_9'), ('M308_10')) v(no_);
Here is a db<>fiddle.
I have a phone number in sql table, i want to add prefix '0' where ever phone number is 10 digit,
but if it is less than 10 or greater than 10 digit then no 0 prefix required.
7863176061
7724269820
2088076157
1992762084
1318912
output
07863176061
07724269820
02088076157
01992762084
1318912
Try this
SELECT CASE LEN(Num) WHEN 10 THEN '0'+cast(Num as varchar(11)) ELSE Num END AS Num
Try this:
select
case when len(yourcolumn) =10
then '0'+ yourcolumn
else yourcolumn end as column
from yourtable
Use CASE expression to check the length.
Query
select
case when len([phone_number]) = 10
then '0' + cast([phone_number] as varchar(20))
else cast([phone_number] as varchar(20)) end
from [your_table_name];
Find demo here
You have to cast the phone number column to varchar if the column datatype is in bigint. Otherwise you can exclude the cast part in the above query.
i think this will be fast,
select
'0' + cast([phone_number] as varchar(20))
from [your_table_name]
where len([phone_number]) = 10 ;
I needed help with something I am not entirely sure how to resolve.
This is my code :
SELECT DISTINCT
Nr_of_Times_Cust_No_Appears=CASE WHEN CAST(a.TV_Code AS Int)-CAST(BB_Code AS Int)=0 THEN COUNT(*) OVER (PARTITION BY BB_Code) ELSE 'Not same' END
FROM table
Basically, the above code is meant to make sure that the substraction of TV Code and BB Code is Zero(0) if not then 'Not Same'. TV Code and BB Code are both varchar that are CAST to Int. The problem lies in the ELSE ' NOT SAME'.
This is the output I get from SQL SERVER Management Studio:
Conversion failed when converting the varchar value 'Not same' to data type int.
What should I do to make it work?
UPDATE: I finally found the workaround.
CAST(COUNT(*) OVER (PARTITION BY XX) AS Varchar)..... It worked!!!!
This is happening because the output of your CASE statement is returning count() (an integer) from THEN 'Not same' (a string) from the ELSE. They need to be the same types. If you cast your count() to a string you will be fine.
SELECT DISTINCT Nr_of_Times_Cust_No_Appears=
CASE WHEN CAST(a.TV_Code AS Int)-CAST(BB_Code AS Int)=0
THEN CAST(COUNT(*) OVER (PARTITION BY BB_Code) AS VARCHAR)
ELSE 'Not same'
END
FROM table
Alternative:
SELECT DISTINCT Nr_of_Times_Cust_No_Appears=
CASE WHEN CAST(a.TV_Code AS Int)-CAST(BB_Code AS Int)=0
THEN COUNT(*) OVER (PARTITION BY BB_Code)
ELSE -1
END
FROM table
In the consuming code i.e. User Interface, if Nr_of_Times_Cust_No_Appears < 0 then show 'Not same'!
The error is simply occurring as your destination column can only have one data type.
The first part of your CASE statement is effictively setting the column type to expect an integer, so when you hit the ELSE section and try to insert Not Same, you're getting the error.
Sample:
SELECT Num
INTO #T
FROM ( SELECT '1' AS Num
UNION
SELECT '2'
) AS val
SELECT CASE WHEN Num = '1' THEN CAST(Num AS INT)
ELSE 'Not 1'
END AS OutputVal
FROM #T
DROP TABLE #T
Gives you:
Conversion failed when converting the varchar value 'Not 1' to data type int.
So you need to insert an acceptable value, which could be NULL:
Sample:
SELECT Num
INTO #T
FROM ( SELECT '1' AS Num
UNION
SELECT '2'
) AS val
SELECT CASE WHEN Num = '1' THEN CAST(Num AS INT)
ELSE NULL
END AS OutputVal
FROM #T
DROP TABLE #T
Check variable Nr_of_Times_Cust_No_Appears. It seems to be int, but you try to set it 'Not same'.
How can I retrieve individual characters of field values (e.g A - C - D from value ACD) and manipulate in SQL Server if I have a column of type varchar(4) with following variable length values.
Column
---
ACD
BC
CD
Thanks
select case when CHARINDEX('A', columnName, 1) > 0 then 1 else 0 end has_a,
case when CHARINDEX('B', columnName, 1) > 0 then 1 else 0 end has_b,
case when CHARINDEX('C', columnName, 1) > 0 then 1 else 0 end has_c,
case when CHARINDEX('D', columnName, 1) > 0 then 1 else 0 end has_d
from tableName
You may use SUBSTRING:
SELECT SUBSTRING('ABC',1,1)
The first param is the input string, the second one is the start index 1-based, and the third is the length of the result.
So if you have a column Col1 in a table Table1:
SELECT SUBSTRING(Col1,1,1),
SUBSTRING(Col1,2,1),
SUBSTRING(Col1,3,1),
SUBSTRING(Col1,4,1)
FROM Table1
You will get an empty string if for instance you have three characters and you try to get the fourth.
Fiddle Example Here
Is there a neat way to apply a mask to a string in a SQL Server query?
I have two tables, one with Phone number stored as varchar with no literals 0155567890 and a phone type, which has a mask for that phone number type: (##) #### ####
What is the best way to return a string (for a merge Document) so that the query returns the fully formatted phone number:
(01) 5556 7890
As noted in the comment, my original answer below will result in terrible performance if used in a large number of rows. i-one's answer is preferred if performance is a consideration.
I needed this also, and thanks to Sjuul's pseudocode, I was able to create a function to do this.
CREATE FUNCTION [dbo].[fx_FormatUsingMask]
(
-- Add the parameters for the function here
#input nvarchar(1000),
#mask nvarchar(1000)
)
RETURNS nvarchar(1000)
AS
BEGIN
-- Declare the return variable here
DECLARE #result nvarchar(1000) = ''
DECLARE #inputPos int = 1
DECLARE #maskPos int = 1
DECLARE #maskSign char(1) = ''
WHILE #maskPos <= Len(#mask)
BEGIN
set #maskSign = substring(#mask, #maskPos, 1)
IF #maskSign = '#'
BEGIN
set #result = #result + substring(#input, #inputPos, 1)
set #inputPos += 1
set #maskPos += 1
END
ELSE
BEGIN
set #result = #result + #maskSign
set #maskPos += 1
END
END
-- Return the result of the function
RETURN #result
END
Just in case someone ever needs a table-valued function.
Approach 1 (see #2 for a faster version)
create function ftMaskPhone
(
#phone varchar(30),
#mask varchar(50)
)
returns table as
return
with ci(n, c, nn) as (
select
1,
case
when substring(#mask, 1, 1) = '#' then substring(#phone, 1, 1)
else substring(#mask, 1, 1)
end,
case when substring(#mask, 1, 1) = '#' then 1 else 0 end
union all
select
n + 1,
case
when substring(#mask, n + 1, 1) = '#' then substring(#phone, nn + 1, 1)
else substring(#mask, n + 1, 1)
end,
case when substring(#mask, n + 1, 1) = '#' then nn + 1 else nn end
from ci where n < len(#mask))
select (select c + '' from ci for xml path(''), type).value('text()[1]', 'varchar(50)') PhoneMasked
GO
Then apply it as
declare #mask varchar(50)
set #mask = '(##) #### ####'
select pm.PhoneMasked
from Phones p
outer apply ftMaskPhone(p.PhoneNum, #mask) pm
Approach 2
I'm going to leave the above version for historical purposes. However, this one has better performance.
CREATE FUNCTION dbo.ftMaskPhone
(
#phone varchar(30),
#mask varchar(50)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH v1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
v2(N) AS (SELECT 1 FROM v1 a, v1 b),
v3(N) AS (SELECT TOP (ISNULL(LEN(#mask), 0)) ROW_NUMBER() OVER (ORDER BY ##SPID) FROM v2),
v4(N, C) AS (
SELECT N, ISNULL(SUBSTRING(#phone, CASE WHEN c.m = 1 THEN ROW_NUMBER() OVER (PARTITION BY c.m ORDER BY N) END, 1), SUBSTRING(#mask, v3.N, 1))
FROM v3
CROSS APPLY (SELECT CASE WHEN SUBSTRING(#mask, v3.N, 1) = '#' THEN 1 END m) c
)
SELECT MaskedValue = (
SELECT c + ''
FROM v4
ORDER BY N
FOR XML PATH(''), TYPE
).value('text()[1]', 'varchar(50)')
);
GO
Schema binding, in combination with this being a single-statement table-valued-function, makes this version eligible for inlining by the query optimizer. Implement the function using a CROSS APPLY as in the example above, or for single values, like this:
SELECT *
FROM dbo.ftMaskPhone('0012345678910', '### (###) ###-####')
Results look like:
MaskedValue
001 (234) 567-8910
This is just what came up in my head. I don't know whether it's the best solution but I think it should be workable.
Make a function with the name applyMask (orso)
Pseudocode:
WHILE currentPosition < Length(PhoneNr) AND safetyCounter < Length(Mask)
IF currentSign = "#"
result += Mid(PhoneNr, currentPosition, 1)
currentPosition++
ELSE
result += currentSign
safetyCounter++
END
END
Return result
As noted by #Sean, SQL Server 2012 and up supports the FORMAT function, which almost gives you what you need, with the following caveats:
It takes a number to format, rather than a VARCHAR. This could be worked around by using a CAST.
The mask as provided ((##) #### ####), coupled with a CAST would remove the leading zero, leaving you with (1) 5556 7890. You could update the mask to (0#) #### ####. Going on a limb that you're representing an Australian phone number, it seems that the leading 0 is always there anyways:
Within Australia, to access the "Number" of a landline telephone in an "Area" other than that in which the caller is located (including a caller using a "Mobile" 'phone), firstly it is necessary to dial the Australian "Trunk Access Code" of 0 plus the "Area" code, followed by the "Local" Number. Thus, the "Full National Number" (FNN) has ten digits: 0x xxxx xxxx.
But ultimately, I would argue that SQL Server is not the best place to handle representation/formatting of your data (as with dates, so with phone numbers). I would recommend doing this client-side using something like Google's libphonenumber. When a phone number is entered into the database, you could store the phone number itself and the country to which it belongs, which you could then use when displaying the phone number (or doing something like calling it or checking for validity).
There is the built in FORMAT function, which almost works. Unfortunately it takes an int as the first parameter, so it strips off the leading zero:
select format(0155567890 ,'(##) #### ####')
(1) 5556 7890
If you need to "mask", rather hide the real value with another, and then "unmask" a string you can try this function, or extend it for that matter. :)
https://stackoverflow.com/a/22023329/2175524
I wanted to hide some information, so i used RIGHT function. It shows only first 4 chars from right side.
CONCAT('xxx-xx-', RIGHT('03466045896', 4))
Above code will show "xxx-xx-5896"