3

I have the following data in a database table column named timeSchedule

00100110
00010100
00110000
00110011

Boolean addition would result in

00110111

Is there a way to do this in sql? Something like select sumboolean(timeSchedule) from myTable

Someone asked for DDL+DML.. here is an example:

CREATE TABLE [dbo].[myTable](
    [musPracticeID] [int] IDENTITY(1,1) NOT NULL,
    [chosenDate] [datetime] NULL,
    [timeSchedule] [nvarchar](50) NULL CONSTRAINT [DF_myTable_schedule]  DEFAULT (N'0000000000000000')
)

INSERT INTO myTable (chosenDate, timeSchedule)
      VALUES (’06/07/2015’, ’01000100’);
bdfios
  • 657
  • 6
  • 17
  • 29
  • eg. database name is db1 – bdfios Jun 08 '15 at 18:18
  • Oracle, sql server, mysql, postgres...? – 6ton Jun 08 '15 at 18:19
  • I'm using sql server – bdfios Jun 08 '15 at 18:19
  • what is the datatype of that column? – ragerory Jun 08 '15 at 18:24
  • Actually I stored the values as String. – bdfios Jun 08 '15 at 18:32
  • No, I'm not wanting to perform binary addition, I need to perform boolean addition. If there is a way to convert the string to a form that would make the boolean addition possible. – bdfios Jun 08 '15 at 18:33
  • Have a look at [this question](http://stackoverflow.com/questions/4833807/how-to-do-bitwise-exclusive-or-in-sql-server-between-two-binary-types). SQL Server has a bitwise OR operator (`|`), but oddly (to me, at least), it can take at most one binary-type operand. Also see the documentation for SQL Server's bitwise operators [here](https://msdn.microsoft.com/en-us/library/ms176122.aspx). – Joe Farrell Jun 08 '15 at 18:35
  • I'm not sure where the comments about "binary" come from. Anyway, possible duplicate of [Aggregate bitfield values with binary OR](http://stackoverflow.com/questions/10225885/aggregate-bitfield-values-with-binary-or)? I'm not confident enough to actually close the question though. –  Jun 08 '15 at 18:44
  • So you have a column with numbers, stored as string, that you want to SUM as booleans... I think the correct answer would be to rethink the approach. – ragerory Jun 08 '15 at 18:46
  • Thanks for your input guys... – bdfios Jun 08 '15 at 18:49
  • @ragerory you are right, I'm actually considering a different strategy but I thought to throw it out there in case there is a quick fix. I'll review the strategy. Thanks! – bdfios Jun 08 '15 at 18:51
  • possible duplicate of [Aggregate bitwise-OR in a subquery](http://stackoverflow.com/questions/3981050/aggregate-bitwise-or-in-a-subquery) – 1010 Jun 08 '15 at 18:56
  • If you stored your values as int you could use this http://stackoverflow.com/a/3981353/4020264 – 1010 Jun 08 '15 at 18:57
  • @hvd I am curious to understand how `binary` comment doesn't make sense to you. Perhaps, I am not aware of such calculations where 1+1 = 10 other than in the binary world. – FutbolFan Jun 08 '15 at 19:11
  • @NepaliRookie But that's not the calculation the OP is asking for at all. Binary addition doesn't give the OP the result included in the question. Logical OR does give that result. –  Jun 08 '15 at 19:12
  • @hvd Yes, but storing a bit field in a string format also doesn't make much sense to me. Maybe I am analyzing this too much. Could you also please post your answer with the use of Logical OR to show the result? Thanks! – FutbolFan Jun 08 '15 at 19:16
  • @NepaliRookie, not just being silly storing bit field as string, fact is, I needed those bits and needed to have a default value. Subsequent data will be stored based on the default value and positions. So, I know I would need to do some conversion but you know, just trying to use a trick to get the job done, hence the seemingly silly approach that I used. – bdfios Jun 08 '15 at 19:22
  • @NepaliRookie I included a link to another question in an earlier comment. 1010 posted another link to another question. Both of those have answers involving bitwise OR already, and at least one of the answers is trivially extended to handle binary strings (by taking the `MAX` of each character position). –  Jun 08 '15 at 22:18

4 Answers4

4

OK, Now that we have the DDL (unfortunately without the DML but only one row). we can provide a solution :-)

firstly! I highly recommend NOT TO USE the solution above, THERE IS NO NEEDS for loops even you not use fixed data length we know the max len (50).

Secondly! If you are going to parse text then you should use SQLCLR and not looping and parsing using T-SQL, in most cases as this one as well.

Third :-) here is simple example for simple solution. I only used the first 10 chars... you can continue to 50... you can use dynamic query in order to create the query if you dont want to write it yourself manually (There are other solutions as well, I recommend to check execution plan and IO used in order to select the best solution for u):

CREATE TABLE [dbo].[myTable](
    [musPracticeID] [int] IDENTITY(1,1) NOT NULL,
    [chosenDate] [datetime] NULL,
    [timeSchedule] [nvarchar](50) NULL CONSTRAINT [DF_myTable_schedule]  DEFAULT (N'0000000000000000')
)
GO
truncate table [myTable]
INSERT INTO myTable (chosenDate, timeSchedule) 
VALUES 
('06/07/2015', '00100110'),
('06/07/2015', '00010100'),
('06/07/2015', '00110000'),
('06/07/2015', '00110011');
GO

select * from myTable
GO

;With MyCTE as (
    select 
        SUBSTRING([timeSchedule],1,1) as c1,
        SUBSTRING([timeSchedule],2,1) as c2,
        SUBSTRING([timeSchedule],3,1) as c3,
        SUBSTRING([timeSchedule],4,1) as c4,
        SUBSTRING([timeSchedule],5,1) as c5,
        SUBSTRING([timeSchedule],6,1) as c6,
        SUBSTRING([timeSchedule],7,1) as c7,
        SUBSTRING([timeSchedule],8,1) as c8,
        SUBSTRING([timeSchedule],9,1) as c9
    from myTable
)
select 
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c1)) > 0 THEN 1 ELSE 0 END)+
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c2)) > 0 THEN 1 ELSE 0 END)+
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c3)) > 0 THEN 1 ELSE 0 END)+
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c4)) > 0 THEN 1 ELSE 0 END)+
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c5)) > 0 THEN 1 ELSE 0 END)+
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c6)) > 0 THEN 1 ELSE 0 END)+
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c7)) > 0 THEN 1 ELSE 0 END)+
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c8)) > 0 THEN 1 ELSE 0 END)+
    CONVERT( NVARCHAR(50),CASE WHEN SUM(CONVERT(INT,c9)) > 0 THEN 1 ELSE 0 END)
from MyCTE
Ronen Ariely
  • 2,336
  • 12
  • 21
  • 1
    Thanks @RonenAriely. This works for me!!! I only changed ...? > 1 to ... > 0, because if the sum is greater than 0 result should be 1, else 0. Thanks everyone for your input. – bdfios Jun 08 '15 at 20:23
  • @bdfios True :-) My mistake it should have been => 1 or just >0 :-) – Ronen Ariely Jun 08 '15 at 20:29
2

First thing you need is a way to get the string and convert it into a #. So, you need to create a new scalar function (borrowed from here).

CREATE FUNCTION [dbo].[BinaryToDecimal]
(
    @Input varchar(255)
)
RETURNS bigint
AS
BEGIN

    DECLARE @Cnt tinyint = 1
    DECLARE @Len tinyint = LEN(@Input)
    DECLARE @Output bigint = CAST(SUBSTRING(@Input, @Len, 1) AS bigint)

    WHILE(@Cnt < @Len) BEGIN
        SET @Output = @Output + POWER(CAST(SUBSTRING(@Input, @Len - @Cnt, 1) * 2 AS bigint), @Cnt)

        SET @Cnt = @Cnt + 1
    END

    RETURN @Output  

END

Then you can simply use:

SUM([dbo].[BinaryToDecimal](timeSchedule))

Then wrap that in another function to convert it back to a string representation. This is a good example.

By the way, storing binary as a string is almost always the wrong approach.

Community
  • 1
  • 1
UnhandledExcepSean
  • 12,504
  • 2
  • 35
  • 51
0

You can use the following query to perform bitwise OR of each 1, 0 character contained in field [timeSchedule] of your table:

;WITH Tally (n) AS
(   
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
    CROSS JOIN (VALUES(0),(0),(0),(0),(0)) b(n)   
), CTE AS (
   SELECT n, MAX(x.c) AS bitwiseOR
   FROM mytable
   CROSS JOIN Tally
   CROSS APPLY (SELECT SUBSTRING([timeSchedule], n, 1)) AS x(c)
   GROUP BY n
)
SELECT ( 
   SELECT CAST(bitwiseOR AS VARCHAR(MAX)) 
   FROM CTE AS t         
   WHERE bitwiseOR <> ''
   ORDER BY n
   FOR XML PATH('')) AS sumBoolean

The idea is to use a tally table in order to 'explode' each character of [timeSchedule] column. Then use MAX to perform bitwise OR operation per bit position. Finally, use FOR XML PATH in order to concatenate all single bits into a string.

Note: This query will work even for variable length values of [timeSchedule], i.e. for any value contained in the column having a length between 1 and 50.

Demo here

Giorgos Betsos
  • 71,379
  • 9
  • 63
  • 98
0

Here are two more solutions :-) the logic is the same. But once I saw my solution in practice, i realized that I dont need to use SUM since we just need to chose MAX. Next since the CHAR 1 is more than the char 0 (char and not number) in most collate (cultures) then we don't need any CONVERT as well and we can just select the MAX from the CHAR. so here are the two solutions:

-- This solution fit all
;With MyCTE as (
    select 
        SUBSTRING([timeSchedule],1,1) as c1,
        SUBSTRING([timeSchedule],2,1) as c2,
        SUBSTRING([timeSchedule],3,1) as c3,
        SUBSTRING([timeSchedule],4,1) as c4,
        SUBSTRING([timeSchedule],5,1) as c5,
        SUBSTRING([timeSchedule],6,1) as c6,
        SUBSTRING([timeSchedule],7,1) as c7,
        SUBSTRING([timeSchedule],8,1) as c8,
        SUBSTRING([timeSchedule],9,1) as c9
    from myTable
)
select 
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c1)))+
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c2)))+
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c3)))+
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c4)))+
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c5)))+
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c6)))+
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c7)))+
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c8)))+
    CONVERT( NVARCHAR(50),MAX(CONVERT(INT,c9)))
from MyCTE

-- MAX char depends on collate (like sorting, comparing)
-- but this solution fit most collate as least, if not all,
-- since "1" bigger than "0"
-- In this solution you need to remember that you will not get the "zero padding"
-- the solution will be in the len of the bigger len
;With MyCTE as (
    select 
        SUBSTRING([timeSchedule],1,1) as c1,
        SUBSTRING([timeSchedule],2,1) as c2,
        SUBSTRING([timeSchedule],3,1) as c3,
        SUBSTRING([timeSchedule],4,1) as c4,
        SUBSTRING([timeSchedule],5,1) as c5,
        SUBSTRING([timeSchedule],6,1) as c6,
        SUBSTRING([timeSchedule],7,1) as c7,
        SUBSTRING([timeSchedule],8,1) as c8,
        SUBSTRING([timeSchedule],9,1) as c9
    from myTable
)
select 
    MAX(c1)+
    MAX(c2)+
    MAX(c3)+
    MAX(c4)+
    MAX(c5)+
    MAX(c6)+
    MAX(c7)+
    MAX(c8)+
    MAX(c9)
from MyCTE
Ronen Ariely
  • 2,336
  • 12
  • 21