Access query to SQL Server view - sql

I am trying to convert an Access query to a SQL Server view. I have 3 boolean columns in the Access query MedTypeHealth, MedTypeSocial and MedTypeEducation.
In Access I use this formula:
MedType: IIf([MedTypeHealth],"H"," ") & IIf([MedTypeSocial],"S"," ") & IIf([MedTypeEducation],"E"," ")
If all 3 flags are set, this returns 'HSE' and if only the Health flag is set, I get me 'H'.
I have the same columns in the SQL Server view.
How can I get the equivalent result in a SQL Server view? What T-SQL functions and code should I use?

SQL Server has no boolean datatype, so you would probably use a small integer value, or a bit.
Then, I would recommend concat_ws() and conditional expressions: it happily ignores null values, which simplifies the case expressions:
concat_ws('',
case when MedTypeHealth = 1 then 'H' end,
case when MedTypeSocial = 1 then 'S' end,
case when MedTypeEducation = 1 then 'E' end
) as MedType

You can use the same IIF function.
Replace double quotes with single quotes to indicate strings in SQL Server
Replace & with + to concatenate strings in SQL Server
You need to explicitly tell the comparison like [MedTypeHealth]=1 in SQL Server
SQL Server does not have boolean. You can use bit (which is 0 or 1) but you cannot do SUM() operation on it. If you need SUM() operation, either use another numeric type (tinyint, smallint, etc.) or do an explicit type cast cast(MedTypeSocial as smallint)
Here is an example you can use:
CREATE TABLE [MyTestTable1] (
[MedTypeHealth] bit
, [MedTypeSocial] bit
, [MedTypeEducation] bit
)
INSERT INTO [MyTestTable1]
VALUES
( 1, 0, 1)
, ( 0, 0, 1)
, ( 1, 1, 1)
, ( 1, 0, 0)
, ( 0, 0, 0)
SELECT
*
, IIf([MedTypeHealth]=1,'H',' ') + IIf([MedTypeSocial]=1,'S',' ') + IIf([MedTypeEducation]=1,'E',' ') WithSpace
, IIf([MedTypeHealth]=1,'H','') + IIf([MedTypeSocial]=1,'S','') + IIf([MedTypeEducation]=1,'E','') WithoutSpace
FROM
[MyTestTable1]
---- drop when done. uncomment first
-- DROP TABLE [MyTestTable1]

Related

How to Find the first occurrence of the ZZ identity and then store the value in variable

Here I want to store the value '202202171100DF' Which is coming after the first occurrence of ZZ.
DECLARE #Element1 VARCHAR(100)='DO#0000000000#ZZ#202202171100DF#ZZ#1#ZZ#20122877GH'
Below query will get the all values after the first occurrence of ZZ. I just want, it should get the first occurrence of ZZ. i.e.'202202171100DF'
SELECT SUBSTRING(#Element1,CHARINDEX('ZZ',#Element1)+3,len(#Element1));
As I mentioned in the comments, this really isn't a task for T-SQL. It has poor string manipulation functionality. There are a couple of methods you could use, though.
The first is similar to the method you attempted, using SUBSTRING and CHARINDEX:
DECLARE #ELEMENT1 VARCHAR(100)='DO#0000000000#ZZ#202202171100DF#ZZ#1#ZZ#20122877GH';
SELECT SUBSTRING(#ELEMENT1, CHARINDEX('ZZ#', #ELEMENT1) + 3, CHARINDEX('#', #ELEMENT1 + '#',CHARINDEX('ZZ#', #ELEMENT1) + 3) - (CHARINDEX('ZZ#', #ELEMENT1) + 3));
This is pretty messy, with all the references to CHARINDEX, but does get the job done.
Another method is the split the string into parts and then return the first row where the "identifier" has the value 'ZZ'. Unfortunately (hopefully until SQL Server 2022) SQL Server's in-built string splitter, STRING_SPLIT doesn't return the ordinal position, so we'll need to use something else such as a JSON splitter. Then you can use a pivot to match the identifier to the value and then get the "first" entry:
DECLARE #ELEMENT1 VARCHAR(100)='DO#0000000000#ZZ#202202171100DF#ZZ#1#ZZ#20122877GH';
WITH Identities AS(
SELECT [key] / 2 AS Entry,
MAX(CASE WHEN [key] % 2 = 0 THEN [Value] END) AS [Identity],
MAX(CASE WHEN [key] % 2 = 1 THEN [Value] END) AS [Value]
FROM OPENJSON(CONCAT('["',REPLACE(#ELEMENT1,'#', '","'),'"]')) OJ
GROUP BY [key] / 2)
SELECT TOP (1)
[Value]
FROM Identities
WHERE [Identity] = 'ZZ'
ORDER BY Entry ASC;
But, again, ideally do this out of T-SQL. If you need to send such data to SQL Server, do so in a normalised format or in properly defined JSON or XML, which SQL Server can natively consume (in all fully supported versions).

How to remove non alphanumeric characters in SQL without creating a function?

I'm trying to remove non alphanumeric characters in multiple columns in a table, and have no permission to create functions nor temporary functions. I'm wonder whether anyone here have any experiences removing non alphanumeric characters without creating any functions at all? Thanks. I'm using MS SQL Server Management Studio v17.9.1
If you have to use a single SELECT query like #Forty3 mentioned then the multiple REPLACEs like #Gordon-Linoff said is probably best (but definitely not ideal).
If you can update the data or use T-SQL, then you could do something like this from https://searchsqlserver.techtarget.com/tip/Replacing-non-alphanumeric-characters-in-strings-using-T-SQL:
while ##rowcount > 0
update user_list_original
set fname = replace(fname, substring(fname, patindex('%[^a-zA-Z ]%', fname), 1), '')
where patindex('%[^a-zA-Z ]%', fname) <> 0
Here is a starting point - you will need to adjust it to accommodate all of the columns which require cleansing:
;WITH allcharcte ( id, textcol1, textcol2, textcol1where, textcol2where )
AS (SELECT id,
CAST(textcol1 AS NVARCHAR(255)),
CAST(textcol2 AS NVARCHAR(255)),
-- Start the process of looking for non-alphanumeric chars in each
-- of the text columns. The returned value from PATINDEX is the position
-- of the non-alphanumeric char and is stored in the *where columns
-- of the CTE.
PATINDEX(N'%[^0-9A-Z]%', textcol1),
PATINDEX(N'%[^0-9A-Z]%', textcol2)
FROM #temp
UNION ALL
-- This is the recursive part. It works through the rows which have been
-- returned thus far processing them for use in the next iteration
SELECT prev.id,
-- If the *where column relevant for each of the columns is NOT NULL
-- and NOT ZERO, then use the STUFF command to replace the char
-- at that location with an empty string
CASE ISNULL(prev.textcol1where, 0)
WHEN 0 THEN CAST(prev.textcol1 AS NVARCHAR(255))
ELSE CAST(STUFF(prev.textcol1, prev.textcol1where, 1, N'') AS NVARCHAR(255))
END,
CASE ISNULL(prev.textcol2where, 0)
WHEN 0 THEN CAST(prev.textcol2 AS NVARCHAR(255))
ELSE CAST(STUFF(prev.textcol2, prev.textcol2where, 1, N'') AS NVARCHAR(255))
END,
-- We now check for the existence of the next non-alphanumeric
-- character AFTER we replace the most recent finding
ISNULL(PATINDEX(N'%[^0-9A-Z]%', STUFF(prev.textcol1, prev.textcol1where, 1, N'')), 0),
ISNULL(PATINDEX(N'%[^0-9A-Z]%', STUFF(prev.textcol2, prev.textcol2where, 1, N'')), 0)
FROM allcharcte prev
WHERE ISNULL(prev.textcol1where, 0) > 0
OR ISNULL(prev.textcol2where, 0) > 0)
SELECT *
FROM allcharcte
WHERE textcol1where = 0
AND textcol2where = 0
Essentially, it is a recursive CTE which will repeatedly replace any non-alphanumeric character (found via the PATINDEX(N'%[^0-9A-Z]%', <column>)) with an empty string (via the STUFF(<column>, <where>, N'')). By replicating the blocks, you should be able to adapt it to any number of columns.
EDIT: If you anticipate having more than 100 instances of non-alphanumeric characters to strip out of any one column, you will need to adjust the MAXRECURSION property ahead of the call.

Invalid argument for function integer IBM DB2

I need to filter out rows in table where numer_lini column has number in it and it is between 100 and 999, below code works just fine when i comment out line where i cast marsnr to integer. However when i try to use it i get error: Invalid character found in a character string argument of the function "INTEGER". when looking at the list seems like replace and translate filters only numbers just fine and select only contains legit numbers (list of unique values is not long so its easy to scan by eye). So why does it fail to cast something? I also tried using integer(marsnr), but it produces the same error. I need casting because i need numeric range, otherwise i get results like 7,80 and so on. As I mentioned Im using IBM DB2 database.
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where numer_lini in (
select marsnr
from (
select
distinct numer_lini marsnr
from alaska.trasa
where case
when replace(translate(numer_lini, '0','123456789','0'),'0','') = ''
then numer_lini
else 'no'
end <> 'no'
)
where cast(marsnr as integer) between 100 and 999
)
fetch first 300 rows only
If you look at the optimized SQL from the Db2 explain, you will see that Db2 has collapsed your code into a single select.
SELECT DISTINCT Q2.NUMER_LINI AS "NUMER_LINI",
Q2.WAR_TRASY AS "WAR_TRASY",
Q2.ID_PRZ1 AS "ID_PRZ1",
Q2.ID_PRZ2 AS "ID_PRZ2",
Q1.NUMER_LINI
FROM ALASKA.TRASA AS Q1,
ALASKA.TRASA AS Q2
WHERE (Q2.NUMER_LINI = Q1.NUMER_LINI)
AND (100 <= INTEGER(Q1.NUMER_LINI))
AND (INTEGER(Q1.NUMER_LINI) <= 999)
AND (CASE WHEN (REPLACE(TRANSLATE(Q1.NUMER_LINI,
'0',
'123456789',
'0'),
'0',
'') = '') THEN Q1.NUMER_LINI
ELSE 'no' END <> 'no')
Use a CASE to force Db2 to do the "is integer" check first. Also, you don't check for the empty string.
E.g. with this table and data
‪create‬‎ ‪TABLE‬‎ ‪alaska‬‎.‪trasa‬‎ ‪‬‎(‪numer_lini‬‎ ‪VARCHAR‬‎(‪10‬‎)‪‬‎,‪‬‎ ‪war_trasy‬‎ ‪INT‬‎ ‪‬‎,‪‬‎ ‪id_prz1‬‎ ‪INT‬‎,‪‬‎ ‪id_prz2‬‎ ‪INT‬‎)‪;
insert into alaska.trasa values ('',1,1,1),('99',1,1,1),('500',1,1,1),('3000',1,1,1),('00300',1,1,1),('AXS',1,1,1);
This SQL works
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where case when translate(numer_lini, '','0123456789') = ''
and numer_lini <> ''
then integer(numer_lini) else 0 end
between 100 and 999
Although that does fail if there is an embedded space in the input. E.g. '30 0'. To cater for that, a regular expressing is probably preferred. E.g.
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where case when regexp_like(numer_lini, '^\s*[+-]?\s*((\d+\.?\d*)|(\d*\.?\d+))\s*$'))
then integer(numer_lini) else 0 end
between 100 and 999

how to convert the output of sub query into numeric

select rptName
from RptTable
where rpt_id in (
select LEFT(Reports, NULLIF(LEN(Reports)-1,-1))
from repoAccess1
where uid = 'VIKRAM'
)
this is my sql query In which i have use the sub query to access selected field
in this sub query returns
select LEFT(Reports, NULLIF(LEN(Reports)-1,-1))
from repoAccess1
where uid = 'VIKRAM'
Returns
1,2
that means the query should be like
select rptName
from RptTable where rpt_id in (1,2)
But i m getting this error
Msg 8114, Level 16, State 5, Line 1
Error converting data type nvarchar to numeric.
could anyone tell me ow to modify to get exact ans
It's a little hard to tell without the concrete table definitions, but I'm pretty sure you're trying to compare different data types to each other. If this is the case you can make use of the CAST or the CONVERT function, for example:
SELECT
[rptName]
FROM [RptTable]
WHERE [rpt_id] IN
(
SELECT
CONVERT(int, LEFT([Reports], NULLIF(LEN([Reports]) - 1, -1)))
FROM [repoAccess1]
WHERE [uid] = 'VIKRAM'
)
UPDATE: Since you have updated your question: The LEFT function returns results of either varchar or nvarchar data type. So the resulting query would be
SELECT
[rptName]
FROM [RptTable]
WHERE [rpt_id] IN('1', '2')
Please note the apostrophes (is this the correct term?) around the values. Since [rpt_id] seems to be of data type int the values cannot implicitly be converted. And that's where the aforementioned CAST or CONVERT come into play.
If I understand correctly, the subquery is returning a single row with a value of '1,2'. This is not a number, hence the error.
Before continuing, let me emphasize that storing values in comma delimited string is not the SQL-way of doing things. You should have one row per id, with proper types and foreign keys defined.
That said, sometimes we are stuck with other people's really bad design decisions. If this is the case, you can use LIKE:
select rptName
from RptTable r
where exists (select 1
from repoAccess1 a
where a.uid = 'VIKRAM' and
',' + a.reports + ',' like '%,' + cast(r.rpt_id as varchar(255)) + ',%'
);
select rptName
from RptTable
where rpt_id in (
select CAST(LEFT(Reports, NULLIF(LEN(Reports)-1,-1)) AS INT) as Val
from repoAccess1
where uid = 'VIKRAM'
)
Your query would work fine when (LEFT(Reports, NULLIF(LEN(Reports)-1,-1)) ) returns either 1 or 2 since SQL Server implicitly converts the varchar value to numeric.
It seems there might be a data issue. One of the data returned by LEFT function is non-numeric. In order to find that particular record you can use isnumeric function. Try like this,
SELECT rptName
FROM RptTable
WHERE rpt_id IN (
SELECT LEFT(Reports, NULLIF(LEN(Reports) - 1, - 1))
FROM repoAccess1
WHERE uid = 'VIKRAM'
AND ISNUMERIC(LEFT(Reports, NULLIF(LEN(Reports) - 1, - 1))) = 1
)

Sql Server Split and Concatenation

I have data in the following format in a sql server database table
[CPOID] [ContractPO] [ContractPOTitle]
1 10-SUP-CN-CNP-0001 Drytech
2 10-SUP-CN-CNP-0002 EC&M
I need to write a stored procedure to generate the following result
[CPOID] [ContractPO] [ContractPOTitle] [ConcatField]
1 10-SUP-CN-CNP-0001 Drytech CNP-0001-Drytech
2 10-SUP-CN-CNP-0002 EC&M CNP-0002-EC&M
where [ConcatField] generate the result using split the last two values of the [ContractPOTitle] column and combine with the [ContractPOTitle]
If the ContractPO field is always the same length, you could just do:
SELECT
CPOID,
ContractPO,
ContractPOTitle,
RIGHT(ContractPO, 8) + '-' + ContractPOTitle as [ConcatField]
FROM MyTable
Assuming that the length of the ContractPO field is not fixed AND we have to rely on stripping out the text after the next to last '-', the following SQL will work. It's a bit ugly, but these types of operations are necessary because there doesn't appear to be a LASTINDEX function available out of the box in SQL Server.
SELECT
CPOID,
ContractPO,
ContractPOTitle,
RIGHT(ContractPO, CHARINDEX('-', REVERSE(ContractPO), CHARINDEX('-', REVERSE(ContractPO)) + 1) - 1) + '-' + ContractPOTitle as [ConcatField]
FROM #myTable