Extract Distinct Special Characters in a Column using SQL Select - sql

I'm having a Table Company. I need to know what are all the Special Characters are in the Company Names.
Consider the Sample Table
S.No. CompanyName
__________________________________
1. 24/7 Customers
2. Rose & Co.
3. Rose Inc. Corp.
4. Rose Pvt. Ltd.,
From the above table I need the Select only the Distinct Special Characters to know what are all the special characters are involved in the CompanyName
The Output of the above table should be /&.,

There is one place where you need to define your "regular" characters
select '%[^a-zA-Z0-9 ]%'
with prm (regular_char)
as
(
select '%[^a-zA-Z0-9 ]%'
)
,cte (special_char,string_suffix)
as
(
select '' as special_char
,CompanyName as string_suffix
from t
union all
select substring (string_suffix,special_char_ind,1) as special_char
,substring (string_suffix,special_char_ind+1,len(string_suffix)) as string_suffix
from (select string_suffix
,nullif(patindex(prm.regular_char,string_suffix),0) as special_char_ind
from cte,prm
where string_suffix <> ''
) t
where special_char_ind is not null
)
select special_char
,ascii(special_char) as ascii_special_char
,count(*) as cnt
from cte
where special_char <> ''
group by special_char
option (maxrecursion 0)
+--------------+--------------------+-----+
| special_char | ascii_special_char | cnt |
+--------------+--------------------+-----+
| | 9 | 1 |
+--------------+--------------------+-----+
| & | 38 | 1 |
+--------------+--------------------+-----+
| , | 44 | 1 |
+--------------+--------------------+-----+
| . | 46 | 5 |
+--------------+--------------------+-----+
| / | 47 | 1 |
+--------------+--------------------+-----+

Try this,
declare #t table(SNo int,CompanyName varchar(30))
insert into #t VALUES
(1,'24/7 Customers')
,(2,'Rose & Co.')
,(3,'Rose Inc. Corp.')
,(4,'Rose Pvt. Ltd.,')
;With CTE as
(
select sno
,stuff(CompanyName,PATINDEX('%[,-#\.&/]%',CompanyName),1,'') CompanyName
,SUBSTRING(CompanyName,PATINDEX('%[,-#\.&/]%',CompanyName),1) spcol from #t
union ALL
select sno,
replace(CompanyName,SUBSTRING(CompanyName,PATINDEX('%[,-#\.&/]%',CompanyName),1),'')
,SUBSTRING(CompanyName,PATINDEX('%[,-#\.&/]%',CompanyName),1)
from cte
where PATINDEX('%[,-#\.&/]%',CompanyName)>0
)
select distinct spcol
from cte

Related

Alternative to CASE WHEN?

I have a table in SQL where the results look something like:
Number | Name | Name 2
1 | John | Derek
1 | John | NULL
2 | Jane | Louise
2 | Jane | NULL
3 | Michael | Mark
3 | Michael | NULL
4 | Sara | Paul
4 | Sara | NULL
I want a way to say that if Number=1, return Name 2 in new column Name 3, so that the results would look like:
Number | Name | Name 2 | Name 3
1 | John | Derek | Derek
1 | John | NULL | Derek
2 | Jane | Louise | Louise
2 | Jane | NULL | Louise
3 | Michael | Mark | Mark
3 | Michael | NULL | Mark
4 | Sara | Paul | Paul
4 | Sara | NULL | Paul
The problem is that I can't say if Number=1, return Name 2 in Name 3, because my table has >100,000 records. I need it to do it automatically. More like "if Number is the same, return Name 2 in Name 3." I've tried to use a CASE statement but haven't been able to figure it out. Is there any way to do this?
Empirically, this seems to work:
SELECT
Number, Name, [Name 2],
MAX([Name 2]) OVER (PARTITION BY Number) [Name 3]
FROM yourTable;
The idea here, if I interpreted your requirements correctly, is that you want to report the non NULL value of the second name for all records as the third name value.
Solution 3, with group by
with maxi as(
SELECT Number, max(Name2) name3
FROM #sample
group by number, name
)
SELECT f1.*, f2.name3
FROM #sample f1 inner join maxi f2 on f1.number=f2.number
Solution 4, with cross apply
SELECT *
FROM #sample f1 cross apply
(
select top 1 f2.Name2 as Name3 from #sample f2
where f2.number=f1.number and f2.Name2 is not null
) f3
you can try this:
Solution 1, with row_number
declare #sample table (Number integer, Name varchar(50), Name2 varchar(50))
insert into #sample
select 1 , 'John' , 'Derek' union all
select 1 , 'John' , NULL union all
select 2 , 'Jane' , 'Louise' union all
select 2 , 'Jane' , NULL union all
select 3 , 'Michael' , 'Mark' union all
select 3 , 'Michael' , NULL union all
select 4 , 'Sara' , 'Paul' union all
select 4 , 'Sara' , NULL ;
with tmp as (
select *, row_number() over(partition by number order by number) rang
from #sample
)
select f1.Number, f1.Name, f1.Name2, f2.Name2 as Name3
from tmp f1 inner join tmp f2 on f1.Number=f2.Number and f2.rang=1
Solution 2, with lag (if your sql server version has lag function)
SELECT
Number, Name, Name2,
isnull(Name2, lag(Name2) OVER (PARTITION BY Number order by number)) Name3
FROM #sample;

SQL: Pick highest and lowest value (int) from one row

I am looking for a way to pick the highest and lowest value (integer) from a single row in table. There are 4 columns that i need to compare together and get highest and lowest number there is.
The table looks something like this...
id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4
1 | John | 5 | 5 | 2 | 1
2 | Peter | 3 | 2 | 4 | 1
3 | Josh | 3 | 5 | 1 | 3
Can you help me, please? Thanks!
You can do this using CROSS APPLY and the VALUES clause. Use VALUES to group all your compared columns and then select the max.
SELECT
MAX(d.data1) as MaxOfColumns
,MIN(d.data1) as MinOfColumns
,a.id
,a.name
FROM YOURTABLE as a
CROSS APPLY (
VALUES(a.col_to_compare1)
,(a.col_to_compare2)
,(a. col_to_compare3)
,(a.col_to_compare4)
,(a. col_to_compare5)
) as d(data1) --Name the Column
GROUP BY a.id
,a.name
Assuming you are looking for min/max per row
Declare #YourTable table (id int,name varchar(50),col_to_compare1 int,col_to_compare2 int,col_to_compare3 int,col_to_compare4 int)
Insert Into #YourTable values
(1,'John',5,5,2,1),
(2,'Peter',3,2,4,1),
(3,'Josh',3,5,1,3)
Select A.ID
,A.Name
,MinVal = min(B.N)
,MaxVal = max(B.N)
From #YourTable A
Cross Apply (Select N From (values(a.col_to_compare1),(a.col_to_compare2),(a.col_to_compare3),(a.col_to_compare4)) N(N) ) B
Group By A.ID,A.Name
Returns
ID Name MinVal MaxVal
1 John 1 5
3 Josh 1 5
2 Peter 1 4
These solutions keep the current rows and add additional columns of min/max.
select *
from t cross apply
(select min(col) as min_col
,max(col) as max_col
from (
values
(t.col_to_compare1)
,(t.col_to_compare2)
,(t.col_to_compare3)
,(t.col_to_compare4)
) c(col)
) c
OR
select *
,cast ('' as xml).value ('min ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as min_col
,cast ('' as xml).value ('max ((sql:column("t.col_to_compare1"),sql:column("t.col_to_compare2"),sql:column("t.col_to_compare3"),sql:column("t.col_to_compare4")))','int') as max_col
from t
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| id | name | col_to_compare1 | col_to_compare2 | col_to_compare3 | col_to_compare4 | min_col | max_col |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 1 | John | 5 | 5 | 2 | 1 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 2 | Peter | 3 | 2 | 4 | 1 | 1 | 4 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
| 3 | Josh | 3 | 5 | 1 | 3 | 1 | 5 |
+----+-------+-----------------+-----------------+-----------------+-----------------+---------+---------+
A way to do this is to "break" apart the data
declare #table table (id int, name varchar(10), col1 int, col2 int, col3 int, col4 int)
insert into #table values (1 , 'John' , 5 , 5 , 2 , 1)
insert into #table values (2 , 'Peter' , 3 , 2 , 4 , 1)
insert into #table values (3 , 'Josh' , 3 , 5 , 1 , 3)
;with stretch as
(
select id, col1 as col from #table
union all
select id, col2 as col from #table
union all
select id, col3 as col from #table
union all
select id, col4 as col from #table
)
select
t.id,
t.name,
agg.MinCol,
agg.MaxCol
from #table t
inner join
(
select
id, min(col) as MinCol, max(col) as MaxCol
from stretch
group by id
) agg
on t.id = agg.id
Seems simple enough
SELECT min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table
Gives you the Min and Max for each column.
Following OP's comment, I believe he may be looking for a min/max grouped by the person being queried against.
So that would be:
SELECT name, min(col1), max(col1), min(col2), max(col2), min(col3), max(col3), min(col4), max(col4) FROM table GROUP BY name

Make single record to multiple records in sql server

I have a records look like below
From two rows, I want to split ShiftPattern values and create multiple records and StartWeek will be created sequentially.
Final Query:
Split ShiftPattern Column and Create multiple records
Increase StartWeek like as 20, 21 to rotation.
Output result
This is what you need. Tested in fiddle.
SQLFiddle Demo
select q.locationid,q.employeeid,
case
when (lag(employeeid,1,null) over (partition by employeeid order by weekshiftpatternid)) is null
then startweek
else startweek + 1
end as rotation ,
q.weekshiftpatternid,
q.shiftyear
from
(
select locationid,employeeid, left(d, charindex(',', d + ',')-1) as weekshiftpatternid ,
startweek,shiftyear
from (
select *, substring(shiftpattern, number, 200) as d from MyTable locationid left join
(select distinct number from master.dbo.spt_values where number between 1 and 200) col2
on substring(',' + shiftpattern, number, 1) = ','
) t
) q
Output
+------------+------------+----------+--------------------+-----------+
| locationid | employeeid | rotation | weekshiftpatternid | shiftyear |
+------------+------------+----------+--------------------+-----------+
| 1 | 10000064 | 20 | 1006 | 2016 |
| 1 | 10000064 | 21 | 1008 | 2016 |
| 1 | 10000065 | 20 | 1006 | 2016 |
| 1 | 10000065 | 21 | 1008 | 2016 |
+------------+------------+----------+--------------------+-----------+
Similar:
In my test table my ID is your EmployeeID or however you want to work it.
SELECT
*,
LEFT(shiftBits, CHARINDEX(',', shiftBits + ',')-1) newShiftPattern,
StartWeek+ROW_NUMBER() OVER(PARTITION BY ID ORDER BY shiftBits ) as newStartWeek
FROM
(
SELECT
SUBSTRING(shiftPattern, number, LEN(shiftPattern)) AS shiftBits,
test2.*
FROM
test2,master.dbo.spt_values
WHERE
TYPE='P' AND number<LEN(shiftPattern)
AND SUBSTRING(',' + shiftPattern, number, 1) = ','
) AS x

How to create a condition for this case?

Sample Table:
Id |Acc_Code|Description |Balance | Acclevel| Acctype| Exttype|
--- -------- ----------------- |-------- |-------- | -------| -------|
1 |SA |Sales | 0.00 | 1 | SA | |
2 |CS |Cost of Sales | 0.00 | 1 | CS | |
3 |5000/001|Revenue | 94.34 | 2 | SA | |
4 |5000/090|Sales(Local) | 62.83 | 2 | SA | |
5 |7000/000|Manufacturing Acc |-250.80 | 2 | CS | MA |
6 |7000/200|Manufacturing Acc | 178.00 | 2 | CS | |
This is a sample data of a temporary table which would be used to be inserted into another temporary table that would calculate the data for Profit and Loss Statement (For Manufacturing related Accounts only).
In this case, the acc_code for Manufacturing accounts start from 7000/000 and separated/partitioned for each following Exttype.
Eg: We start from the exttype of MA and based on its acclevel (could be 2 or more) until the next exttype.
The idea is we get the manufacturing accounts by SELECT FROM tmp_acc_list WHERE acc_code BETWEEN #start_acc_code (7000/000 in this case) AND #end_acc_code (the data before the next exttype)
I don't know what the exttype is, I'm still learning the tables.
How do we create the #end_acc_code part out from this sample table?
So here is a all in one script.
I created Your table for test:
create table #tmp_acc_list(
Id numeric,
Acc_Code nvarchar(100),
Acclevel numeric,
Acctype nvarchar(100),
Exttype nvarchar(100));
GO
insert into #tmp_acc_list(Id, Acc_Code, Acclevel, Acctype, Exttype)
select 1 , 'SA', 1,'SA', null union all
select 2 , 'CS', 1,'CS', null union all
select 3 , '5000/001', 2,'SA', null union all
select 4 , '5000/090', 2,'SA', null union all
select 5 , '7000/000', 2,'CS', 'MA' union all
select 6 , '7000/200', 2,'CS', null
;
Then comes the query:
with OrderedTable as -- to order the table is Id is not an order
(
select
t.*, ROW_NUMBER() over (
order by id asc --use any ordering You need here
)
as RowNum
from
#tmp_acc_list as t
),
MarkedTable as -- mark with common number
(
select
t.*,
Max(case when t.Exttype is null then null else t.RowNum end)
over (order by t.RowNum) as GroupRownum
from OrderedTable as t
),
GroupedTable as -- add group Exttype
(
select
t.Id, t.Acc_Code, t.Acclevel, t.Acctype, t.Exttype,
max(t.Exttype) over (partition by t.GroupRownum) as GroupExttype
from MarkedTable as t
)
select * from GroupedTable where GroupExttype = 'MA'
Is this what You need?
select *
from
(
select Id, Acc_Code
from tmp_acc_list
where Acc_Code = '7000/000'
) s
cross join tmp_acc_list a
cross apply
(
select top 1 x.Id, x.Acc_Code
from tmp_acc_list x
where x.Id >= a.Id
and x.AccLevel = a.AccLevel
and x.Acctype = a.Acctype
and x.Exttype = ''
order by Id desc
) e
where a.Id between s.Id and e.Id

Data Matching with SQL and assigning Identity ID's

How to write a query that will match data and produce and identity for it.
For Example:
RecordID | Name
1 | John
2 | John
3 | Smith
4 | Smith
5 | Smith
6 | Carl
I want a query which will assign an identity after matching exactly on Name.
Expected Output:
RecordID | Name | ID
1 | John | 1X
2 | John | 1X
3 | Smith | 1Y
4 | Smith | 1Y
5 | Smith | 1Y
6 | Carl | 1Z
Note: The ID should be unique for every match. Also, it can be numbers or varchar.
Can somebody help me with this? The main thing is to assign the ID's.
Thanks.
How about this:
with temp as
(
select 1 as id,'John' as name
union
select 2,'John'
union
select 3,'Smith'
union
select 4,'Smith'
union
select 5,'Smith'
union
select 6,'Carl'
)
SELECT *, DENSE_RANK() OVER
(ORDER BY Name) as NewId
FROM TEMP
Order by id
The first part is for testing purposes only.
Please try:
SELECT *,
Rank() over (order by Name ASC)
FROM table
This structure seems to work:
CREATE TABLE #Table
(
Department VARCHAR(100),
Name VARCHAR(100)
);
INSERT INTO #Table VALUES
('Sales','michaeljackson'),
('Sales','michaeljackson'),
('Sales','jim'),
('Sales','jim'),
('Sales','jill'),
('Sales','jill'),
('Sales','jill'),
('Sales','j');
WITH Cte_Rank AS
(
SELECT [Name],
rw = ROW_NUMBER() OVER (ORDER BY [Name])
FROM #Table
GROUP BY [Name]
)
SELECT a.Department,
a.Name,
b.rw
FROM #Table a
INNER JOIN Cte_Rank b
ON a.Name = b.Name;