Get Count Between Comma and First Character - sql

In my table, the values appear like this in a column:
Names
----------------
Doe,John P
Woods, Adam
Hart, Keeve
Hensen,Sarah J
Is it possible to get a count of space between the comma and first character after it? Expected result:
Names |Count_of_spaces_before_next_character
-----------------|--------------------------------------
Doe,John P | 0
Woods, Adam | 1
Hart, Keeve | 5
Hensen,Sarah J | 0
Thanks for any direction, much appreciate it !

You may try with the following statement:
Table:
CREATE TABLE Data (Names varchar(1000))
INSERT INTO Data
(Names)
VALUES
('Doe,John P'),
('Woods, Adam'),
('Hart, Keeve'),
('Hensen,Sarah J')
Statement:
SELECT Names, LEN(After) - LEN(LTRIM(After)) AS [Count]
FROM (
SELECT
Names,
RIGHT(Names, LEN(Names) - CHARINDEX(',', Names)) AS After
FROM Data
) t
Result:
Names Count
Doe,John P 0
Woods, Adam 1
Hart, Keeve 5
Hensen,Sarah J 0

You can remove everything up to the comma and then measure the length after trimming off the spaces:
select len(rest) - len(ltrim(rest))
from t cross apply
(values (stuff(name, 1, charindex(',', name + ','), ''))
) v(rest);
The + ',' handles the case where there is no comma in name.
Here is a db<>fiddle.

Updated
Even shorter & Cleaner
select names
,patindex('%[^ ]%',stuff(names,1,charindex(',',names),''))-1 as Count_of_spaces_before_next_character
from mytab
-
+----------------+---------------------------------------+
| names | Count_of_spaces_before_next_character |
+----------------+---------------------------------------+
| Doe,John P | 0 |
| Woods, Adam | 1 |
| Hart, Keeve | 5 |
| Hensen,Sarah J | 0 |
| Hello World | 0 |
+----------------+---------------------------------------+
SQL Fiddle

Building off of Gordon's answer. If you are not familiar with table constructor approach in cross apply, you might find this more readable. Hope the select in cross apply clears your confusion around how it can behave like a subquery. Also, I don't think you need to pad the name column with an additional ',' because charindex will return 0 if it doesn't find that character.
select name, len(rest) - len(ltrim(rest))
from t1
cross apply (select stuff(name, 1, charindex(',', name), '') as rest) t2

Logic: LEN(string after first comma) - LEN(LTRIM(string after first comma))
SELECT Name, LEN (SUBSTRING(Names,CHARINDEX(',',Names,1)+1 ,LEN(Names) - CHARINDEX(',',Names,1)) ) - LEN(LTRIM(SUBSTRING(Names,CHARINDEX(',',Names,1)+1 , LEN(Names) - CHARINDEX(',',Names,1))))
FROM #Table

Related

Group by portion of field

I have a field in a PostgreSQL table, name, with this format:
JOHN^DOE
BILLY^SMITH
FIRL^GREGOIRE
NOEL^JOHN
and so on. The format is LASTNAME^FIRSTNAME. The table has ID, name, birthdate and sex fields.
How can I do a SQL statement with GROUP BY FIRSTNAME only ? I have tried several things, and I guess regexp_match could be the way, but I don't know how to write a correct regular expression for this task. Can you help me ?
I would recommend split_part():
group by split_part(mycol, '^', 1)
Demo on DB Fiddle:
mycol | split_part
:------------ | :---------
JOHN^DOE | JOHN
BILLY^SMITH | BILLY
FIRL^GREGOIRE | FIRL
NOEL^JOHN | NOEL
Use regexp_replace. Note that '^' needs to be escaped, since in many regexp dialects it means the beginning of the line or or the string. Extending your example with one more name, and using group by on the first field:
select
count(*)
, regexp_replace(tmp_col, '\^.*', '')
from
(values
('JOHN^DOE')
, ('BILLY^SMITH')
, ('FIRL^GREGOIRE')
, ('NOEL^JOHN')
, ('JOHN^SMITH')
)
as tmp_table(tmp_col)
group by regexp_replace(tmp_col, '\^.*', '')
;
Prints:
count | regexp_replace
-------+----------------
1 | BILLY
2 | JOHN
1 | NOEL
1 | FIRL
(4 rows)
To group by on the second field, use a similar regex:
select
count(*)
, regexp_replace(tmp_col, '.*\^', '')
from
(values
('JOHN^DOE')
, ('BILLY^SMITH')
, ('FIRL^GREGOIRE')
, ('NOEL^JOHN')
, ('JOHN^SMITH')
)
as tmp_table(tmp_col)
group by regexp_replace(tmp_col, '.*\^', '')
;
Prints:
count | regexp_replace
-------+----------------
1 | JOHN
1 | GREGOIRE
1 | DOE
2 | SMITH
(4 rows)

Unpack all arrays in a JSON column SQL Server 2019

Say I have a table Schema.table with these columns
id | json_col
on the forms e.g
id=1
json_col ={"names":["John","Peter"],"ages":["31","40"]}
The lengths of names and ages are always equal but might vary from id to id (size is at least 1 but no upper limit).
How do we get an "exploded" table - a table with a row for each "names", "ages" e.g
id | names | ages
---+-------+------
1 | John | 31
1 | Peter | 41
2 | Jim | 17
3 | Foo | 2
.
.
I have tried OPENJSON and CROSS APPLY but the following gives any combination of names and ages which is not correct, thus I need to to a lot of filtering afterwards
SELECT *
FROM Schema.table
CROSS APPLY OPENJSON(Schema.table,'$.names')
CROSS APPLY OPENJSON(Schema.table,'$.ages')
Here's my suggestion
DECLARE #tbl TABLE(id INT,json_col NVARCHAR(MAX));
INSERT INTO #tbl VALUES(1,N'{"names":["John","Peter"],"ages":["31","40"]}')
,(2,N'{"names":["Jim"],"ages":["17"]}');
SELECT t.id
,B.[key] As ValueIndex
,B.[value] AS PersonNam
,JSON_VALUE(A.ages,CONCAT('$[',B.[key],']')) AS PersonAge
FROM #tbl t
CROSS APPLY OPENJSON(t.json_col)
WITH(names NVARCHAR(MAX) AS JSON
,ages NVARCHAR(MAX) AS JSON) A
CROSS APPLY OPENJSON(A.names) B;
The idea in short:
We use OPENJSON with a WITH clause to read names and ages into new json variables.
We use one more OPENJSON to "explode" the names-array
As the key is the value's position within the array, we can use JSON_VALUE() to read the corresponding age-value by its position.
One general remark: If this JSON is under your control, you should change this to an entity-centered approach (array of objects). Such a position dependant storage can be quite erronous... Try something like
{"persons":[{"name":"John","age":"31"},{"name":"Peter","age":"40"}]}
Conditional Aggregation along with applying CROSS APPLY might be used :
SELECT id,
MAX(CASE WHEN RowKey = 'names' THEN value END) AS names,
MAX(CASE WHEN RowKey = 'ages' THEN value END) AS ages
FROM
(
SELECT id, Q0.[value] AS RowArray, Q0.[key] AS RowKey
FROM tab
CROSS APPLY OPENJSON(JsonCol) AS Q0
) r
CROSS APPLY OPENJSON(r.RowArray) v
GROUP BY id, v.[key]
ORDER BY id, v.[key]
id | names | ages
---+-------+------
1 | John | 31
1 | Peter | 41
2 | Jim | 17
3 | Foo | 2
Demo
The first argument for OPENJSON would be a JSON column value, but not a table itself

SQL concat rows with same name value

Let say I got table like that
Name | Stage | Date
-------------------
A | 1st | 03092014
A | 2nd | 04092014
A | 3rd | 05092014
B | 1st | 06092014
B | 2nd | 08092014
C | 1st | 03092014
I wonder how to write SQL code wich would concat rows with same names and I will get something like that
Name | Stage | Date
----------------------+-----------------------------
A | 1st , 2nd, 3rd | 03092014 04092014 05092014
B | 1st, 2nd | 06092014 08092014
C | 1st | 03092014
Do I need to run through table with for cycle or is there better way to do that?
UPD:
I found out that I need to use this queries in Excel
You can use GROUP_CONCAT for this:
SELECT Name
, GROUP_CONCAT(Stage) AS Stages
, GROUP_CONCAT(Date) AS Dates
FROM my_table
GROUP BY Name;
With respect to your question - I am assuming you are using MS SQL Server 2008 or higher to get he desired output
I would suggest to use CROSS APPLY here to concat the data -
Assumed Your Table name - temptable
SELECT distinct tblMain.Name, substring(stages, 1, len(stages)-1) as [Stage],substring(dates, 1, len(dates)-1) as [Date]
FROM temptable tblMain
CROSS APPLY (
SELECT LTRIM(RTRIM(Stage)) + ','
FROM temptable tblDup1 WITH(NOLOCK)
WHERE tblDup1.Name= tblMain.Name
FOR XML PATH('')
) t1 (stages)
CROSS APPLY (
SELECT LTRIM(RTRIM(Date)) + ' '
FROM temptable tblDup2 WITH(NOLOCK)
WHERE tblDup2.Name= tblMain.Name
FOR XML PATH('')
) t2 (dates)
Working FIDDLE OUTPUT

Mysql: How to get every rows that have more than a certain number of decimal after the dot

I have a table that contains float values.
table
+ id | value |
+--------|---------|
+ 1 | 19.22 |
+ 2 | 32.333 |
+ 3 | 1.2332 |
+ 4 | 0.22334 |
+ 5 | 4.55 |
I want to extract every row that contains more than 3 decimal after the dot.
The result I would expect is:
+ id | value |
+--------|---------|
+ 2 | 32.333 |
+ 3 | 1.2332 |
+ 4 | 0.22334 |
Cast the value column as a varchar and use string comparison.
This worked for me:
SELECT *
FROM table
WHERE column <> ROUND (column,2)
or:
SELECT *
FROM table
WHERE column <> CAST (column AS DECIMAL(36,2))
This regex (MySQL 5.0+) worked for me, based on the data you provided:
SELECT t.*
FROM YOUR_TABLE t
WHERE t.`value` REGEXP '[0-9]+.[0-9][0-9][0-9]+'
Reference:
http://www.regular-expressions.info/
This worked for me
SELECT *
FROM `table`
WHERE LENGTH(SUBSTR(`value`,INSTR(`value`,"."))) >3
Counting the decimal .01 is 3, .011 is 4
Try this:
select value, LEN(value) - CHARINDEX('.', value) as DecimalCount from Table
where LEN(value) - CHARINDEX('.', value) > 2
NOTE: I'm not using mySQL, I have MSSQL. let me know if this works for you - just noticed you asked for a mySQL solution.
UPDATE: Felt bad for answering for the wrong platform. A bit of research got me this for mySQL:
select * from Table
where LENGTH(SUBSTRING_INDEX(value, '.', -1)) > 2
SELECT *
FROM table t
WHERE floor(t.columnname)!=t.columnname
SQL Server:
To extract values with more than 3 decimal places from the "quantity" column in a SQL table with the "quantity" column using float data type, you can use the following query:
SELECT *
FROM `table`
WHERE ROUND(quantity, 3) <> quantity;

MySQL GROUP_CONCAT headache

For performance,I need to set a limit for the GROUP_CONCAT,
and I need to know if there are rows not included.
How to do it?
EDIT
Let me provide a contrived example:
create table t(qid integer unsigned,name varchar(30));
insert into t value(1,'test1');
insert into t value(1,'test2');
insert into t value(1,'test3');
select group_concat(name separator ',')
from t
where qid=1;
+----------------------------------+
| group_concat(name separator ',') |
+----------------------------------+
| test1,test2,test3 |
+----------------------------------+
But now,I want to group 2 entries at most,and need to know if there is some entry not included in the result:
+----------------------------------+
| group_concat(name separator ',') |
+----------------------------------+
| test1,test2 |
+----------------------------------+
And I need to know that there is another entry left(in this case it's "test3")
this should do the trick
SELECT
SUBSTRING_INDEX(group_CONCAT(name) , ',', 2) as list ,
( if(count(*) > 2 , 1 , 0)) as more
FROM
t
WHERE
qid=1
How are you going to set the limit? And what performance issues will it solve?
You can get the number of rows in a group using count(*) and compare it to the limit.