none
extraer una sola letra de un campo RRS feed

  • Pregunta

  • Hola.

    quiero saber como puedo obtener de resultado una sola letra de mi campo (en SQL Server).

    tengo esto en mi base de datos :

      Quantity

       226 g

      198 g

      54gr

      500ml

    y lo que quiero hacer es que quede asi:

    Quantity1   Quantity2

       226               g

       198               g

        54                gr

        500             ml

    intente usar substring pero no me funciono con los que estan pegados y no tienen espacio, ¿hay alguna manera de separar esos valores?


    martes, 7 de mayo de 2019 23:00

Respuestas

  • Hola Augusto Mejía:

    Una aproximación a lo que necesitas.

    Te puedes crear una función como esta, que te resuelve una tabla con una fila y una columna por los números que contenga la cadena que le pases.

    CREATE function [dbo].[fnNum](@campo varchar(max))
    returns @table table (col int)
    as
    begin
    
    ;with c (v)as (
    	select 1 as v
    		union all 
    	select 1 as v
    ), c2 as (select b.v from c cross join c as b
    ), c3 as (select b.v from c2 cross join c2 as b
    ), c4 as (select b.v from c3 cross join c3 as b
    ), c5 as (select b.v from c4 cross join c4 as b
    ), long as 
    (select top (ISNULL(Datalength(@campo),0)) ROW_NUMBER() over (order by (select null)) as N
    from c5),
    sep as (
    		select SUBSTRING(@campo,N,1) as car
    			from long 
    ),
    esNum as (
    		select case when ISNUMERIC(car)=1 then car else null end as digito
    		    from sep
    ), unidos as (
     SELECT STUFF(
    (
        SELECT ' '+CAST(esNum.digito AS VARCHAR(MAX)) 
        
        FROM esnum
        WHERE esNum.digito is not null
               FOR XML PATH('')
    ), 1, 1, '') as v
    
    FROM esNum AS R /* Y AQUÍ */
    
    ) insert into @table 
    select CAST(REPLACE(v,' ','') as int) as n from unidos
    group by v;
    
    return ;
    end;
    
    

    Una vez la tienes, actualizas la cache del intellisense (Menú Editar -- Intellisense -- Actualizar cache local)

    Con la función ya creada y dispuesta.

    declare @table table (quantity varchar(100))
    insert into @table (quantity)
    values
    ('226 g'),
    ('198 g'),
    ('54gr'),
    ('500ml');
    
    select v.COL as Quantity1,
    LTRIM(right(t.quantity,(len(t.quantity)-(LEN(v.col))))) as Quantity2
    from @table t 
    cross apply fnNum(t.quantity) v

    Solo tienes que llamarla como si fuera una tabla con el operador cross apply y resolverá para cada valor de quantity los números que contenga.

    De la salida de la misma, solo tienes que leer los caracteres que quedan a la derecha y ya tienes las dos columnas.

    Salida

    Espero te ayude

    • Propuesto como respuesta eRiver1 miércoles, 8 de mayo de 2019 16:32
    • Marcado como respuesta Pablo RubioModerator jueves, 16 de mayo de 2019 14:47
    miércoles, 8 de mayo de 2019 16:17
  • Hola

    intenta con esto

    primero una clase o diccionario lo que te sea mas facil

        public class cadenas
        {
            public string numero { get; set; }
            public string denominacion { get; set; }
        }

    luego una funcion que separe el numero de los caracteres

            private cadenas Separa(string cad)
            {
                cadenas c = new cadenas();
                bool Letra = false;
                string numero = "";
                string Denominador = "";
                foreach (char chr in cad)
                {
                    if((Char.IsDigit(chr) || chr=='.' ) && !Letra)
                    {
                        numero += chr;
                    }
                    else
                    {
                        Denominador += chr;
                    }
                }
                c.numero = numero.Trim();
                c.denominacion = Denominador.Trim();
                return c;
            }

    y se llama la funcion

     List<cadenas> cadenas = new List<cadenas>();
     cadenas.Add( Separa("193.90Kg"));

    • Propuesto como respuesta eRiver1 miércoles, 8 de mayo de 2019 16:32
    • Marcado como respuesta Pablo RubioModerator jueves, 16 de mayo de 2019 14:47
    miércoles, 8 de mayo de 2019 3:38
  • Deleted
    viernes, 10 de mayo de 2019 12:00
  • Hola, doy por sentado que la parte de texto (g,Gr,ml) solo son dos dígitos o pueden ser mas? máximo?

    Si el máximo es controlable, veamos como ejemplo 2, pero podemos ampliarlo a 3,4 lo que desees siempre que no sea demasiado variable podemos usar una simple consulta para sacar los campos.

    declare @campo as char(10)='226 ml'
    
    select SUBSTRING(replace(@campo,' ',''),1,LEN(replace(@campo,' ',''))-(CASE WHEN ISNUMERIC( substring(reverse(replace(@campo,' ','')),2,1))=1 THEN '1' ELSE 2 END)) as Quantity1,
    RIGHT(replace(@campo,' ',''),CASE WHEN ISNUMERIC( substring(reverse(replace(@campo,' ','')),2,1))=1 THEN '1' ELSE 2 END) as Quantity2

    Puedes probarlos cambiando el dato en el valor @campo para que veas que funciona.

    El resto es sencillo, le damos la vuelta a la cadena, quitando espacios, luego comprobamos que la posición 2 (al darle la vuelta es la penúltima de tu cadena) es un número (con la función isnumeric) si lo es es que tu parte Quantity2 es de longitud 1 si es texto y tenemos 2 sería longitud 2, con eso cogemos la cadena original, le quitamos espacios, y con un substring y un len para saber el tamaño total, sabemos encontrar la posicion donde termina el numérico, ya que hemos averiguado el tamaño de la parte de texto..

    Espero que te sea de ayuda...



    JM Claudio Dba/Consultor SQL/BI Pasiona - Spain


    miércoles, 8 de mayo de 2019 14:15

Todas las respuestas

  • Yo he hecho eso pero llevando la tabla a Visual Foxpro, allí es fácil, se hacen varios pasos porque al final el campo Quantity1 debe ser numérica y Quantity2 texto. Luego se migra a SQL de regreso.
    martes, 7 de mayo de 2019 23:14
  • Hola

    intenta con esto

    primero una clase o diccionario lo que te sea mas facil

        public class cadenas
        {
            public string numero { get; set; }
            public string denominacion { get; set; }
        }

    luego una funcion que separe el numero de los caracteres

            private cadenas Separa(string cad)
            {
                cadenas c = new cadenas();
                bool Letra = false;
                string numero = "";
                string Denominador = "";
                foreach (char chr in cad)
                {
                    if((Char.IsDigit(chr) || chr=='.' ) && !Letra)
                    {
                        numero += chr;
                    }
                    else
                    {
                        Denominador += chr;
                    }
                }
                c.numero = numero.Trim();
                c.denominacion = Denominador.Trim();
                return c;
            }

    y se llama la funcion

     List<cadenas> cadenas = new List<cadenas>();
     cadenas.Add( Separa("193.90Kg"));

    • Propuesto como respuesta eRiver1 miércoles, 8 de mayo de 2019 16:32
    • Marcado como respuesta Pablo RubioModerator jueves, 16 de mayo de 2019 14:47
    miércoles, 8 de mayo de 2019 3:38
  • Hola, doy por sentado que la parte de texto (g,Gr,ml) solo son dos dígitos o pueden ser mas? máximo?

    Si el máximo es controlable, veamos como ejemplo 2, pero podemos ampliarlo a 3,4 lo que desees siempre que no sea demasiado variable podemos usar una simple consulta para sacar los campos.

    declare @campo as char(10)='226 ml'
    
    select SUBSTRING(replace(@campo,' ',''),1,LEN(replace(@campo,' ',''))-(CASE WHEN ISNUMERIC( substring(reverse(replace(@campo,' ','')),2,1))=1 THEN '1' ELSE 2 END)) as Quantity1,
    RIGHT(replace(@campo,' ',''),CASE WHEN ISNUMERIC( substring(reverse(replace(@campo,' ','')),2,1))=1 THEN '1' ELSE 2 END) as Quantity2

    Puedes probarlos cambiando el dato en el valor @campo para que veas que funciona.

    El resto es sencillo, le damos la vuelta a la cadena, quitando espacios, luego comprobamos que la posición 2 (al darle la vuelta es la penúltima de tu cadena) es un número (con la función isnumeric) si lo es es que tu parte Quantity2 es de longitud 1 si es texto y tenemos 2 sería longitud 2, con eso cogemos la cadena original, le quitamos espacios, y con un substring y un len para saber el tamaño total, sabemos encontrar la posicion donde termina el numérico, ya que hemos averiguado el tamaño de la parte de texto..

    Espero que te sea de ayuda...



    JM Claudio Dba/Consultor SQL/BI Pasiona - Spain


    miércoles, 8 de mayo de 2019 14:15
  • Hola Augusto Mejía:

    Una aproximación a lo que necesitas.

    Te puedes crear una función como esta, que te resuelve una tabla con una fila y una columna por los números que contenga la cadena que le pases.

    CREATE function [dbo].[fnNum](@campo varchar(max))
    returns @table table (col int)
    as
    begin
    
    ;with c (v)as (
    	select 1 as v
    		union all 
    	select 1 as v
    ), c2 as (select b.v from c cross join c as b
    ), c3 as (select b.v from c2 cross join c2 as b
    ), c4 as (select b.v from c3 cross join c3 as b
    ), c5 as (select b.v from c4 cross join c4 as b
    ), long as 
    (select top (ISNULL(Datalength(@campo),0)) ROW_NUMBER() over (order by (select null)) as N
    from c5),
    sep as (
    		select SUBSTRING(@campo,N,1) as car
    			from long 
    ),
    esNum as (
    		select case when ISNUMERIC(car)=1 then car else null end as digito
    		    from sep
    ), unidos as (
     SELECT STUFF(
    (
        SELECT ' '+CAST(esNum.digito AS VARCHAR(MAX)) 
        
        FROM esnum
        WHERE esNum.digito is not null
               FOR XML PATH('')
    ), 1, 1, '') as v
    
    FROM esNum AS R /* Y AQUÍ */
    
    ) insert into @table 
    select CAST(REPLACE(v,' ','') as int) as n from unidos
    group by v;
    
    return ;
    end;
    
    

    Una vez la tienes, actualizas la cache del intellisense (Menú Editar -- Intellisense -- Actualizar cache local)

    Con la función ya creada y dispuesta.

    declare @table table (quantity varchar(100))
    insert into @table (quantity)
    values
    ('226 g'),
    ('198 g'),
    ('54gr'),
    ('500ml');
    
    select v.COL as Quantity1,
    LTRIM(right(t.quantity,(len(t.quantity)-(LEN(v.col))))) as Quantity2
    from @table t 
    cross apply fnNum(t.quantity) v

    Solo tienes que llamarla como si fuera una tabla con el operador cross apply y resolverá para cada valor de quantity los números que contenga.

    De la salida de la misma, solo tienes que leer los caracteres que quedan a la derecha y ya tienes las dos columnas.

    Salida

    Espero te ayude

    • Propuesto como respuesta eRiver1 miércoles, 8 de mayo de 2019 16:32
    • Marcado como respuesta Pablo RubioModerator jueves, 16 de mayo de 2019 14:47
    miércoles, 8 de mayo de 2019 16:17
  • Deleted
    viernes, 10 de mayo de 2019 12:00