2015-01-01 20 views
6

Ho 2 stringhe che contengono solo 0 e 1. Voglio una stringa di risultati che abbia per bit OR il loro carattere per carattere.Il modo migliore per OR 2 stringhe di 0,1

DECLARE @str1 nvarchar; 
DECLARE @str2 nvarchar; 
SET @str1= '11001100'; 
SET @str2= '00100110'; 

-- I want result to be : 11101110 

La dimensione della stringa è variabile. Posso usare un ciclo for e OR i caratteri uno per uno. ma il numero di stringhe è variabile e la loro dimensione può essere superiore a 1 milione ... Esiste un modo migliore del ciclo FOR?

+3

Perché queste stringhe invece di solo dati binari? Stai usando un byte per rappresentare un bit. In ogni caso, il CLR probabilmente sarebbe di gran lunga il modo migliore. –

+0

la lunghezza massima della stringa è un milione o il numero di stringhe è un milione e la lunghezza massima è breve? – Beth

risposta

4

Idealmente si sarebbe codificando questo come binario.

11001100 è un singolo byte 0xCC.

La memorizzazione come varchar significa che sono necessari 8 byte e dichiarati come nvarchar in 16 byte.

Si potrebbe quindi utilizzare anche CLR e operatori bit a bit.

Rispondere alla domanda che hai chiesto usando una funzione CLR probabilmente sarebbe ancora di gran lunga il modo migliore.

using System; 
using System.Data.SqlTypes; 
using Microsoft.SqlServer.Server; 

public partial class UserDefinedFunctions 
{ 
    [SqlFunction] 
    public static SqlString StringOr(SqlChars a, SqlChars b) 
    { 
     if (a.Length != b.Length) 
     { 
      throw new Exception("Strings should be the same length."); 
     } 

     char[] c = new char[a.Length]; 

     for(int i =0; i < a.Length; i++) 
     { 
      c[i] = (a[i] == '0' && b[i] == '0' ? '0' : '1'); 
     } 

     return (SqlString)(new SqlChars(c));   
    } 
} 

enter image description here

1

provare il seguito

DECLARE @str1 nvarchar(10); 
DECLARE @str2 nvarchar(10); 
DECLARE @result nvarchar(10) = ''; 
declare @counter1 as int = 1; 
SET @str1= '11001100'; 
SET @str2= '00100110'; 
while @counter1 <= len(@str1) 
begin 
if (cast(substring(@str1,@counter1,1) as int) + cast(substring(@str2,@counter1,1) as int) >= 1) 
set @result += '1' 
else 
set @result += '0' 
set @counter1 += 1 
end 

print @result 
+1

Hai visto questa riga in questione ** Esiste un modo migliore rispetto al loop ** –

+1

Ops, non l'ho visto. ma può essere all'interno della funzione definita. – Mohammed

2

Variante senza utilizzare il ciclo - puro approccio di set-based (con CTE ricorsiva) e, pertanto, dovrebbe essere abbastanza efficiente rispetto a qualsiasi tipo di circuito. È possibile utilizzare questa funzione per unire o applicarlo agli altri insiemi di dati (tabelle o visualizzazioni)

-- function to split binary string to result-set 
alter function [dbo].[SplitStringToResultSet] (@value varchar(max), @size int) 
returns table 
as return 
with r as (
    select right(value, 1) [bit] 
    , left(value, len(value)-1) [value] 
    , 0 [pos] 
    from (select rtrim(cast(
     case 
      when len(@value) > @size then left(@value, @size) 
      when len(@value) < @size then @value + replicate('0', @size - len(@value)) 
      else @value 
    end as varchar(max))) [value]) as j 
union all 
select right(value, 1) 
, left(value, len(value)-1) 
, pos + 1 
from r where value > '') 

select cast([bit] as int) [bit], [pos] from r 

-- usage ------------------------------------------------- 
declare 
    @OR varchar(20) = '', 
    @AND varchar(20) = ''; 

select @OR = @OR + cast(n1.[bit] | n2.[bit] as varchar(1)) 
, @AND = @AND + cast(n1.[bit] & n2.[bit] as varchar(1)) 
-- XOR etc 
from [dbo].[SplitStringToResultSet] ('11001100', 8) n1 
full join [dbo].[SplitStringToResultSet] ('00100110', 8) as n2 on n1.[pos] = n2.[pos] 
order by n1.pos desc 

select @OR [OR], @AND [AND] 

risultati

OR   AND 
-------------------- 
11101110 00000100 
+1

Esistono operatori OR bit a bit e AND (|, &). Penso che tu possa usarli al posto delle tue formule. '|' <=> 'n1 + n2 - n1 * n2'. '&' <=> 'n1 * n2'. –

+0

grazie mille per il suggerimento!implementato :) –

+0

Poiché si sta analizzando la stringa nella funzione in ogni caso, sarebbe logico riscrivere la funzione per accettare due parametri Valore1 e Valore2 e OR all'interno della funzione durante la relativa analisi. Quindi potresti evitare il 'FULL JOIN'. BTW, l'analisi della stringa utilizzando CTE ricorsiva è più lenta di altri metodi, come l'utilizzo di una tabella Numbers: http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-2 –

1

domanda e soluzioni molto cool. Aggiungo altro utilizzando XML: -convert stringa boths in XML dove ogni carattere è un nodo -Fare loro valori di tabella con bit e ordinale -join da ordinali aggiungendo bit

DECLARE @str1 nvarchar(max) 
DECLARE @str2 nvarchar(max) 
declare @s1xml xml 
declare @s2xml xml 

SET @str1= '11001100' 
SET @str2= '00100110' 
set @s1xml =(select cast(replace(replace(@str1,'1','<n>1</n>'),'0','<n>0</n>') as xml)) 
set @s2xml =(select cast(replace(replace(@str2,'1','<n>1</n>'),'0','<n>0</n>') as xml)) 


select case when a.bit+b.bit = 0 then 0 else 1 end from 
(select n.value('.','int') bit, 
     n.value('for $i in . return count(../*[. << $i]) + 1', 'int') position 
from @s1xml.nodes('//n') as T1(n)) a 
join 
(select n.value('.','int') bit, 
     n.value('for $i in . return count(../*[. << $i]) + 1', 'int') position 
from @s2xml.nodes('//n') as T2(n)) b 
ON a.position=b.position 
for xml path('') 
0

Supponendo la lunghezza massima della stringa è non un milione ma un numero molto più basso, vorrei usare una tabella di ricerca con 2 colonne e 2 righe di lunghezza massima della stringa con le stringhe di caratteri e valori binari corrispondenti. Quindi è possibile unire le due stringhe a 2 istanze della tabella di ricerca e utilizzare la funzione OR bit a bit sui risultati.

select bitstr1, bitstr2, b1.bin, b2.bin, b1.bin | b2.bin as OR_result 
from 
tblMillion inner join 
tblLkpBin b1 on 
bitstr1 = b1.str inner join 
tblLkpBin b2 on 
bitstr2 = b2.str