Related
I have PostgreSQL SQL that should look for a backslash in a column called source_username and if it finds the backslash, it should replace the current value of the source_username column with the same value without the characters before the backslash.
For example:
before source_username: domain\username
after source_username: username
with os_user as (
select source_username from itpserver.managed_incidents mi;
),
osUserWithoutDomain as (
select (
case when (select * from os_user) ilike '%\\%' and (select position('-' in (select * from os_user))>= 1) and (select length((select * from os_user)) != (select position('-' in (select * from os_user))) + 1)
then (
select substring(
(select * from os_user),(select position('\' in (select * from os_user)) + 1),(select length((select * from os_user)) - 1)
))
else ((select * from os_user))
end
)
)
UPDATE itpserver.managed_incidents SET source_username = replace(source_username, (select * from os_user), (select * from osUserWithoutDomain)),
description = replace(description , (select * from os_user), (select * from osUserWithoutDomain)),
additional_info = replace(additional_info , (select * from os_user), (select * from osUserWithoutDomain)),
typical_behavior = replace(typical_behavior , (select * from os_user), (select * from osUserWithoutDomain)),
raw_description = replace(raw_description , (select * from os_user), (select * from osUserWithoutDomain));
This SQL works fine when I have only one row in the table.
If I have multiple rows, I need to specify the row that I want to work with by adding where id = <id>
I wish to iterate all the relevant rows (all the rows that source_username contains backslash) and on each row to perform the SQL above.
I tried to do this with LOOP:
create or replace function fetcher()
returns void as $$
declare
emp record;
begin
for emp in select *
from itpserver.managed_incidents
order by id
limit 10
loop
raise notice '%', emp.id;
<my sql> where id = emp.id
end loop;
end;
$$language plpgsql;
select fetcher();
However, I get an error because I don't think it likes the 'with' statement.
Any idea how can I do it?
It's far simpler than that. You need to use the SUBSTR and STRPOS functions. Take a look at the results of this query.
https://dbfiddle.uk/9-yPKn6E
with os_user (source_username) as (
select 'domain\username'
union select 'mydomain\joe'
union select 'janet'
)
select u.source_username
, strpos(u.source_username, '\')
, substr(u.source_username, strpos(u.source_username, '\') + 1)
from os_user u
source_username
strpos
substr
domain\username
7
username
janet
0
janet
mydomain\joe
9
joe
What you need is:
UPDATE itpserver.managed_incidents
SET source_username = substr(source_username, strpos(source_username, '\') + 1)
, description = replace(description , source_username, substr(source_username, strpos(source_username, '\') + 1))
, additional_info = replace(additional_info , source_username, substr(source_username, strpos(source_username, '\') + 1))
, typical_behavior = replace(typical_behavior , source_username, substr(source_username, strpos(source_username, '\') + 1))
, raw_description = replace(raw_description , source_username, substr(source_username, strpos(source_username, '\') + 1));
This is based on lengthy experience with SQL Server and some quick document searches for Postgresql. The UPDATE statement may not work as I expect.
SQL by design/default works on complete data sets. It thus eliminates LOOPS entirely from the language - they are not needed. (Well not quite there are recursive queries). Your task is accomplished in a single update statement with a simple regular expression. See documentation String Functions:
update managed_incidents
set source_username = regexp_replace(source_username,'.*\\(.*)','\1');
Demo here.
Main Take away: Drop procedural logic terminology (for, loop, if then, ...) from your SQL vocabulary. (you choose alternatives with case.)
I have a column email with multiple delimiters like space ,/ , .
email
/john#thundergroup.com.mi/chris#cup.com.ey
r.Info#bc.com / rudi.an#yy.com
Dal#pema.com/Al#ama.com
/randi#mv.com
zul#sd.com/sat#sd.com/ faze#sd.com
My query:
select email,
CASE WHEN CHARINDEX(' ', email) > 0 THEN SUBSTRING(email, 0, CHARINDEX(' ', email)) ELSE
email END as Emailnew
FROM table
my output:
/john#thundergroup.com.mi/chris#cup.com.ey
r.Info#bc.com
Dal#pema.com/Al#ama.com
/randi#mv.com
zul#sd.com/sat#sd.com/ faze#sd.com
Please suggest changes so that in a single query I'm able to extract email
To get the first email always, you can try this below logic-
DEMO HERE
SELECT
CASE
WHEN CHARINDEX('/',email,2) = 0 THEN REPLACE(email,'/','')
ELSE REPLACE(SUBSTRING(email,0,CHARINDEX('/',email,2)),'/','')
END
FROM your_table
Output will be-
john#thundergroup.com.mi
r.Info#bc.com
Dal#pema.com
randi#mv.com
zul#sd.com
On modern SQL Servers try something like:
-- Setup...
create table dbo.Foo (
FooID int not null identity primary key,
Email nvarchar(100)
);
insert dbo.Foo (Email) values
('/john#thundergroup.com.mi/chris#cup.com.ey'),
('r.Info#bc.com / rudi.an#yy.com'),
('Dal#pema.com/Al#ama.com'),
('/randi#mv.com'),
('zul#sd.com/sat#sd.com/ faze#sd.com');
go
-- Demo...
select FooID, [Email]=value
from dbo.Foo
outer apply (
select top 1 value
from string_split(translate(Email, ' /', ';;'), ';')
where nullif(value, '') is not null
) Splitzville;
Which yields:
FooID Email
1 john#thundergroup.com.mi
2 r.Info#bc.com
3 Dal#pema.com
4 randi#mv.com
5 zul#sd.com
Requirements:
SQL Server 2016 and later for string_split().
SQL Server 2017 and later for translate().
If you want the first email only, use patindex():
select email,
left(email, patindex('%[^a-zA-Z0-9#.]%', email + ' ') - 1) as Emailnew
from table;
The pattern (a-zA-Z0-9#.) are valid email characters. You may have additional ones that you care about.
Unfortunately, I notice that some of your lists start with delimiter characters. In my opinion, the above works correctly by returning an empty value. That said, your desired results are to get the second value in that case.
So, you have to start the search at the first valid email character:
select t.email,
left(v.email1, patindex('%[^-_a-zA-Z0-9#.]%', v.email1 + ' ') - 1) as Emailnew
from t cross apply
(values (stuff(t.email, 1, patindex('%[-_a-zA-Z0-9#.]%', t.email) - 1, ''))) v(email1);
Here is a db<>fiddle.
Hi all I have a an where clause like below
select * from table1
where colum1 IN (?parameter)
when I pass the values to the parameter they show up like below
('1,2,3') but to execute the query I need to change the values as ('1','2','3')
is there a way to replace the commas with single quotes comma in IN clause directly?
There is one hack to do what you want, using like:
select *
from table1
where ',' || column1 || ',' like '%,' || (?parameter) || ',%';
This functions, but it will not make use of an index on column1. You should think about other solutions, such as:
Parsing the string into a table variable.
Using in with a fixed number of parameters.
Storing the values in a table.
There may be other Oracle-specific solutions as well.
Use MS SQL, you can convert it into table value and using join for your condition, I am not familiar oracle but you can find same way to do it.
DECLARE #IDs varchar(max) ='1,2,3';
;WITH Cte AS
(
SELECT
CAST('<ID>' + REPLACE( #IDs, ',' , '</ID><ID>') + '</ID>' AS XML) AS IDs
)
SELECT '''' + Split.a.value('.', 'VARCHAR(100)') +'''' AS ID FROM Cte
CROSS APPLY Cte.IDs.nodes('/ID') Split(a)
You can achieve it using with clause. Logic here is to convert each comma separated value into different row.
with temp_tab as (
select replace(regexp_substr(parameter, '[^,]+',1, level),'''','') as str
from dual
connect by level<= length(regexp_replace(parameter, '[^,]+'))+1 )
select * from table1 where column1 in (select str from temp_tab);
I'm converting a stored procedure from MySql to SQL Server. The procedure has one input parameter nvarchar/varchar which is a comma-separated string, e.g.
'1,2,5,456,454,343,3464'
I need to write a query that will retrieve the relevant rows, in MySql I'm using FIND_IN_SET and I wonder what the equivalent is in SQL Server.
I also need to order the ids as in the string.
The original query is:
SELECT *
FROM table_name t
WHERE FIND_IN_SET(id,p_ids)
ORDER BY FIND_IN_SET(id,p_ids);
The equivalent is like for the where and then charindex() for the order by:
select *
from table_name t
where ','+p_ids+',' like '%,'+cast(id as varchar(255))+',%'
order by charindex(',' + cast(id as varchar(255)) + ',', ',' + p_ids + ',');
Well, you could use charindex() for both, but the like will work in most databases.
Note that I've added delimiters to the beginning and end of the string, so 464 will not accidentally match 3464.
You would need to write a FIND_IN_SET function as it does not exist. The closet mechanism I can think of to convert a delimited string into a joinable object would be a to create a table-valued function and use the result in a standard in statement. It would need to be similar to:
DECLARE #MyParam NVARCHAR(3000)
SET #MyParam='1,2,5,456,454,343,3464'
SELECT
*
FROM
MyTable
WHERE
MyTableID IN (SELECT ID FROM dbo.MySplitDelimitedString(#MyParam,','))
And you would need to create a MySplitDelimitedString type table-valued function that would split a string and return a TABLE (ID INT) object.
A set based solution that splits the id's into ints and join with the base table which will make use of index on the base table id. I assumed the id would be an int, otherwise just remove the cast.
declare #ids nvarchar(100) = N'1,2,5,456,454,343,3464';
with nums as ( -- Generate numbers
select top (len(#ids)) row_number() over (order by (select 0)) n
from sys.messages
)
, pos1 as ( -- Get comma positions
select c.ci
from nums n
cross apply (select charindex(',', #ids, n.n) as ci) c
group by c.ci
)
, pos2 as ( -- Distinct posistions plus start and end
select ci
from pos1
union select 0
union select len(#ids) + 1
)
, pos3 as ( -- add row number for join
select ci, row_number() over (order by ci) as r
from pos2
)
, ids as ( -- id's and row id for ordering
select cast(substring(#ids, p1.ci + 1, p2.ci - p1.ci - 1) as int) id, row_number() over (order by p1.ci) r
from pos3 p1
inner join pos3 p2 on p2.r = p1.r + 1
)
select *
from ids i
inner join table_name t on t.id = i.id
order by i.r;
You can also try this by using regex to get the input values from comma separated string :
select * from table_name where id in (
select regexp_substr(p_ids,'[^,]+', 1, level) from dual
connect by regexp_substr(p_ids, '[^,]+', 1, level) is not null );
Having a Mysql database, I have to select all ( TUser.Name, TAccount.Name ) pairs if TUser.name not exists in variable #UserNames.
If exists, I have to select only the ( TUser.Name, TAccount.Name ) pairs where TUser.name in #UserNames.
Something like the last line of the below query :
DECLARE #UserNames = "Alpha, Beta, Gama";
SELECT User.Name
, Account.Name
FROM TUser
, TAccount
, TUserAccount
WHERE TAccount.ID = TUserAccount.AccountID
AND TUserAccount.UserID = User.ID
-- please rewrite this line
AND TUser.Name IN ( IFNULL ( ( SELECT ID FROM TUser WHERE Name IN #UserNames ) , ( SELECT ID FROM TUser ) ) )
Thank you in advance !
You can't return mutually exclusive result sets with your criteria, without using a IF statement:
SELECT #sum := SUM(FIND_IN_SET(u.name, #UserNames))
FROM TUSER u
IF #sum > 0 THEN
SELECT u.name,
a.name
FROM TUSER u
JOIN TUSERACCOUNT ua ON ua.userid = u.id
JOIN TACCOUNT a ON a.id = ua.accountid
WHERE FIND_IN_SET(u.name, #UserNames) > 0
ELSE
SELECT u.name,
a.name
FROM TUSER u
JOIN TUSERACCOUNT ua ON ua.userid = u.id
JOIN TACCOUNT a ON a.id = ua.accountid
END IF;
You could make that work as a PreparedStatement, MySQL's dynamic SQL, but you still need to run a query to know if you need to return all or a subset.
References:
FIND_IN_SET
IF statements
SELECT
User.Name,
Account.Name
FROM TUser
JOIN TAccount
ON TAccount.Name = TUser.Name
JOIN TUserAccount
ON TUserAccount.AccountID = TAccount.ID
AND TUserAccount.UserID = TUser.ID
LEFT JOIN (
SELECT ID FROM TUser WHERE Name IN #UserNames
) AS UsersFound
ON TUser.ID = UsersFound.ID
Something like that? I haven't tested it though.
Ok, so this is what I would call 'hacks' rather than normall database querying, and I would disrecommend getting in the position you have to deal with in the first place. The right way to deal with a list of items like this is to have your application language parse it into a nice list you can use to build a normal, regular SQL IN list, like so:
TUser.Name IN ('Name1', 'Name2',...., 'NameX')
But anyway, if you want to stick to the original problem and your database is mysql, you can do it in a remotely sane way in a single query like this:
SET #UserNames := 'Alpha,Beta,Gama';
SELECT TUser.Name
, TAccount.Name
FROM TUser
INNER JOIN TUserAccount
ON TUser.ID = TUserAccount.UserID
INNER JOIN TAccount
ON TUserAccount.AccountID = TAccount.ID
WHERE FIND_IN_SET(TUser.Name, #UserNames)
OR (SELECT COUNT(*)
FROM TUser.Name
WHERE FIND_IN_SET(TUser.Name, #UserNames)) = 0
Note that I did change the input data somewhat - I don't have any spaces behind the commas. If that is unacceptable, simply change each occurrence of #UserNames in the query with REPLACE(#UserNames, ', ', ','). Also please note that it's performance down the drain as it is impossible to use any indexes on TUser.Name to filter for specific users.
I already mentioned that you really should make a proper IN list of your data. And you can do so directly in SQL too (dynamic SQL):
SET #UserNames := 'Alpha, Beta, Gama';
SET #stmt := CONCAT(
' SELECT TUser.Name'
,' , TAccount.Name'
,' FROM TUser'
,' INNER JOIN TUserAccount'
,' ON TUser.ID = TUserAccount.UserID'
,' INNER JOIN TAccount'
,' ON TUserAccount.AccountID = TAccount.ID'
,' WHERE TUser.Name IN (''', REPLACE(#UserNames, ', ', ''',''') , ''')'
,' OR (SELECT COUNT(*) '
,' FROM TUser.Name'
,' WHERE TUser.Name IN (''', REPLACE(#UserNames, ', ', ''',''') , ''')) = 0'
)
PREPARE stmt FROM #stmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
(Note that in this case, I could use the user name list with spaces unaltered)