How to select records testing bits in BINARY column? - sql

I have 2 tables: one of them contains column binary(128), where every bit is some flag. Also I have other table contains list of bit positions which need to check in query.
This is example how I do the query for one bit.
How to do it universally, i.e. to select records testing bits in positions from the second table?
Should it be a function? what?
DECLARE #nByteNum integer
DECLARE #nBitNumInByte integer
DECLARE #nMask integer
DECLARE #nBigBitNum integer
declare #t table(id int not null identity, id1 int, banner binary(128))
declare #bitpositions table(id int not null identity, position int)
insert into #bitpositions(position) values(8)
insert into #bitpositions(position) values(24)
insert into #bitpositions(position) values(30)
insert into #t(id1, banner)
select 1, 0x0
union all
select 1, 0x000100FF
union all
select 1, 0x010200FF
union all
select 10, 0x010208
union all
select 10, 0x000100
union all
select 10, 0x040000
select * from #t
-- This is for one bit
SET #nBigBitNum= 24
SET #nByteNum= #nBigBitNum/8
SET #nBitNumInByte= #nBigBitNum % 8 -- 0,1...6,7
SET #nMask = POWER(2, #nBitNumInByte ) -- 128,64,... 2,1
SET #nByteNum= #nByteNum +1
select * from #t where SUBSTRING(banner, #nByteNum,1)&#nMask=#nMask

Bitwise operations in SQL Server do not work on BINARY data in the way that you are expecting. They work on integer datatypes only as noted on the MSDN page for & (Bitwise AND).
The general syntax for checking bitmasked values in a query is: WHERE {Column} & {BitMaskedValue} = {BitMaskedValue}.
Look at the following examples using your desired scenario of trying to find records that have bit positions 8 and 16 turned on.
-- 24 = 16 + 8
SELECT 24 & 16 -- 16
SELECT 24 & 8 -- 8
SELECT 24 & 17 -- 16
SELECT 24 & 32 -- 0
SELECT 24 & 26 -- 24

Related

Moving calculations using SQL

I need to calculate a new column using moving calculations.
For example, I have a table:
A
B
10
15
11
14
12
13
I need to calculate new column where the 1st value is calculated like 5000/10*15, the 2nd value is (5000 / 10 * 15) / 11 * 14, the 3rd one is ((5000 / 10 * 15) / 11 * 14) / 12 * 13 and so on. Where 5000 is a random value and in the future I will use it like a parameter in a stored procedure.
I know, that in Excel for example we can reffer to the previous calculated cell. How can it be calculated using SQL?
Thank you!
create table #test (A int,B int)
insert into #test values(10,15),(11,14),(12,13)
declare #seed int=5000;
;with temp as (
select A,B,row_number() over(order by a) rn from #test
),
cte as
(
select #seed/A*B calculated,rn from temp where rn=1
union all
select c. calculated/t.A*t.B,t.rn from temp t
join cte c on t.rn=c.rn+1
)
select * from cte
There is a warning in the docs that reads:
If there are multiple assignment clauses in a single SELECT statement,
SQL Server does not guarantee the order of evaluation of the
expressions. Note that effects are only visible if there are
references among the assignments.
It means there is no guarantee that it will evaluate the expression left-to-right. For this code:
declare #a int, #b int;
select #a = 2, #b = #a * 3;
select #a, #b;
The result could be 2, 6 (#a = ... evaluated first) or 2, NULL (#b = ... evaluated first).

Difference between two columns into separate rows for each value between the two

I'm having trouble explaining my problem but I have this table
ID START END
1 10 12
2 30 31
3 11 13
and want something like this:
ID NUMBER
1 10
1 11
1 12
2 30
2 31
3 11
3 12
3 13
I need the all unique whole numbers between the two columns transform into separate rows.
Here's how I want the transformation to look like
I haven't tried anything because I don't even know how to call such procedure so any help is appreciated
If you don't have a numbers table (highly recommended), you can use an ad-hoc tally table in concert with a CROSS APPLY
Example
Select A.ID
,B.Number
From YourTable A
Cross Apply ( Select Top ([End]-[Start]+1)
Number=[START]-1+Row_Number() Over (Order By (Select NULL))
From master..spt_values n1, master..spt_values n2
) B
Returns
ID Number
1 10
1 11
1 12
2 30
2 31
3 11
3 12
3 13
In SQL Server, you can use a recursive CTE:
with cte as (
select id, [start] as number, [end] as end_number
from t
union all
select id, number + 1
from cte
where number < end_number
)
select id, number
from cte;
Note: If the span can exceed 100, you need option (maxrecursion) for the query.
Recursive CTEs are usually a bit slower than a numbers table. However, I find them much faster than I would expect.
--create table NewTable
--(
--ID int,
--NUMBER int
--)
DECLARE #ID nvarchar(50)
declare #START int
declare #END int
DECLARE Cursor_Name CURSOR
FOR
select ID, [START], [End] from tblSequences
OPEN Cursor_Name
FETCH NEXT FROM Cursor_Name INTO #ID,#START,#END
WHILE ##FETCH_STATUS = 0
begin
WHILE #START<=#END
begin
insert into NewTable (ID, NUMBER) Values (#ID, #START)
set #START = #START+1
end
FETCH NEXT FROM Cursor_Name INTO #ID,#START,#END
end
CLOSE Cursor_Name
DEALLOCATE Cursor_Name
select * from NewTable

comparing bit array to bit mask in tsql

I've been working with tsql for quite a while now but I've never seen binary anding or oring in WHERE clause. Now I'm developing new application that would benefit from applying a bitmask.
Lets say I have 16 product grades. Each grade is representrd by a bit position in a bit[] column. So grade A109 would be 0000000000000001, grade B704 would be 0000001000000000, grade V64 is 0100000000000000 and so on. Any grade can only have single 1 in its array column. Now let's say I can turn each of those 3 grades into one another in manufacturing process. So my bit mask for this search would be 0100001000000001. How would I write a WHERE clause to list items of all those 3 grades?
I did some more research and the best solution is to compare masks with bitwise AND operator like this
WHERE mask1 & mask2 <> 0
This is easy, simple and cohesive.
First time for me too. Interesting.
declare #A109 int = 1;
declare #B704 int = 512;
declare #V64 int = 16384;
declare #Xx int = 32;
declare #mask int = 16897; --#A109+#B704+#V64
create table #MyData (
name char(2),
value int);
insert #MyData
values ('a', #A109), ('b1', #B704), ('b2',#B704), ('c', #Xx);
select
name,
value
from #MyData
where (value & #mask) in (#A109, #B704, #V64);
drop table #MyData;
It seems you can't do bitwise operations on binary data! "In a bitwise operation, only one expression can be of either binary or varbinary data type".
In your UI allow the user to select as many grades as (s)he likes. Behind the scenes each grade maps to an int - the Grading table from MarkD's solution. Sum these ints in the application. Pass the total to the SP.
SQL:
create procedure dbo.GetMaskedList #mask int = 0;
...
WHERE BinGrading = #mask;
UI:
int mask = 0;
foreach(item in selectList)
{
mask += item.BinScore;
}
exec GetMaskedList #mask = mask;
In this case, the WHERE clause would be quite simple as you would deal with the decimal (base 10) counterparts of the binary numbers... Where it gets cute, is on the JOIN. To clarify, the approach does not use a bit[] column or a BINARY type - just INT.
Hope you find this helpful.
DECLARE #G1 INT = 1, -- 0000001
#G2 INT = 2, -- 0000010
#G3 INT = 4, -- 0000100
#G4 INT = 8, -- 0001000
#G5 INT = 16, -- 0010000
#G6 INT = 32, -- 0100000
#G7 INT = 64 -- 1000000
;WITH Grading (Grade, BinScore) AS
(
SELECT 'G1', 1 UNION ALL
SELECT 'G2', 2 UNION ALL
SELECT 'G3', 4 UNION ALL
SELECT 'G4', 8 UNION ALL
SELECT 'G5', 16 UNION ALL
SELECT 'G6', 32 UNION ALL
SELECT 'G7', 64
)
,Product (ProductName, BinGrading) AS
(
SELECT 'Foobar', 73 UNION ALL
SELECT 'Franglesnap', 3 UNION ALL
SELECT 'Mebble', 32
)
SELECT *
FROM Product
WHERE BinGrading = (#G1 + #G4 + #G7)
-- Alternatively...
--SELECT *
--FROM Product P
--JOIN Grading G ON P.Bingrading & G.BinScore > 0

Find missing numerical values in range [duplicate]

This question already has answers here:
How can we find gaps in sequential numbering in MySQL?
(16 answers)
Closed 8 years ago.
I've read several articles that one of the mistakes a common programmer does is not using SQL's potential and since then I started searching for replacing parts of my code with SQLish solutions rather than fetching data and processing with a programming language, although I'm a real rookie with SQL.
Say I have a table randomly populated with values from 0 to 10 and I want to know which values are missing in this range.
For example, the table consists these values: 0, 1, 3, 4, 5, 7, 8, 9.
The query should return: 2, 6, 10.
[F5] solution (assuming sql server):
-- table with id=0..10
drop table #temp
GO
create table #temp (
id int not null identity(0,1),
x int
)
GO
insert into #temp (x) values(0)
GO 11
-- your number:
drop table #numbers
GO
select
*
into #numbers
from (
select 0 as n union all select 1 union all select 3 union all select 4 union all select 5 union all select 7 union all select 8 union all select 9
) x
GO
-- result:
select
*
from #temp t
left join #numbers n
on t.id=n.n
where 1=1
and n.n is null
This solution uses SQL-Server-Syntax (but AFAIK only GO is specific to the SQL Server Management Studio)
I would join against a table valued function that gets you all numbers in a certain range (example fiddle):
CREATE FUNCTION dbo.GetNumbersInRange(#Min INT, #Max INT)
RETURNS #trackingItems TABLE (Number INT)
AS BEGIN
DECLARE #counter INT = #Min
WHILE (#counter <= #Max)
BEGIN
INSERT INTO #trackingItems (Number) SELECT #counter
SELECT #counter = #counter + 1
END
RETURN
END
GO
As an example I have set up a table that contains some numbers (with gaps)
CREATE TABLE MyNumbers (Number INT)
INSERT INTO MyNumbers (Number)
SELECT 1
UNION
SELECT 2
UNION
SELECT 4
UNION
SELECT 5
UNION
SELECT 7
UNION
SELECT 8
To find the missing numbers you can use a LEFT JOIN like this
SELECT
AllNumbers.Number
FROM GetNumbersInRange(1, 10) AS AllNumbers
LEFT JOIN MyNumbers ON AllNumbers.Number = MyNumbers.Number
WHERE MyNumbers.Number IS NULL

get specific rows of table given a rule SQL Server 2008

I have a table like:
ID NAME VAL
----------------------
1 a1*a1 90052
2 a1*a2 236
3 a1*a3 56
4 a1*a4 6072
5 a1*a5 1004
6 a2*a2 4576
7 a2*a3 724
8 a2*a4 230
9 a2*a5 679
10 a3*a3 5
11 a3*a4 644
12 a3*a5 23423
13 a4*a4 42354
14 a4*a5 10199
15 a5*a5 10279
Given a number given S = 5, I want to query
the rows wth id: 1,6,10,13,15
they are a1*a1,a2*a2,a3*a3,a4*a4 and a5*a5
I would like something like:
INSERT #NEW_TABLE (ID,NAME,Value) (
SELECT ordinal, NAME, VAL FROM myTable where id = 1,6,10,13,15)
to get
ID NAME VAL
----------------------
1 a1*a1 90052
2 a2*a2 4576
3 a3*a3 5
4 a4*a4 42354
5 a5*a5 10279
Is there a way to do this for any given S, Maybe wth dynamic sql?
I was getting the formula and I got this:
S=5
ID formula
1 1
6 1+S
10 1+S+ (S-1)
13 1+S+ (S-1) + (S-2)
15 1+S+ (S-1) + (S-2) + (S-3)
Is there a way to do this inside a case or a while loop?
This worked in testing.
You can just inner join on #Tab to limit your results. You probably also want to add some traps for values below 3, which I haven't done.
The basic process is
Declare your #s value
Insert the first two rows since they will always be the same
In a loop, insert one row at a time with an incrementing difference
Loop exits once it has run #s-2 times
Try:
DECLARE #Tab Table (id INT)
DECLARE #S int = 5,
#ct int
DECLARE #cur int = (1 + #S)
INSERT INTO #Tab SELECT 1
INSERT INTO #Tab SELECT (1 + #S)
SET #ct = 1
WHILE #ct <= #S - 2
BEGIN
SET #cur = #cur + (#S - #ct)
INSERT INTO #Tab SELECT #cur
SET #ct = #ct + 1
END
SELECT * FROM #Tab
ORDER BY id
To use this in your query, you can do either:
SELECT ordinal, NAME, VAL
FROM myTable
WHERE id IN (SELECT id FROM #Tab)
-- OR
SELECT ordinal, NAME, VAL
FROM myTable t
INNER JOIN #tab t2
ON t2.id = t.id