Имитировать 128-разрядное целое число без знака в SQL и C #, используя 64-разрядное значение со знаком?

1

Возьмите этот сценарий: у вас есть несколько перечислений флагов в С#, связанных с (и фактически сгенерированных) таблицами Enum-ish в SQL Server. Скажите, что вы являетесь дистрибьютором, и вы разрешаете своим реселлерам указывать, в какие штаты США они отправляются. Будучи блестящим и элегантным инженером-программистом, вы реализовали их как бит-комбинируемое значение флага для сохранения хранилища:

create table USState (
    StateID bigint, StateAbbr char(2), StateName varchar(50))
/* insert all US States + DC into USState, StateIDs must be in powers of two */ 
/* StateID 0 reserved for 'None': */
create procedure GetStatesByFlag (@StateFlags bigint) as
declare @StateIDs table
(
StateID bigint,
primary key (StateID)
)
insert into @StateIDs 
    select StateID 
    from USState
    where @StateFlags & StateID != 0
        or (@StateFlags = 0 and StateID = 0)

select s.StateID, s.StateAbbr, s.StateName 
from 
    USState s join 
    @StateIDs si 
    on si.StateID = s.StateID

Сладкое. Вы можете включать/исключать динамически как в SQL, так и в С# с помощью побитовой логики, которая позволяет мгновенно убрать списки флажков и выбирать списки в Asp.NET, сохраняя при этом только одно 64-разрядное число, чтобы сохранить любую комбинацию выбранных. И вам не нужен неиндексируемый оператор сравнения в ваших процедурах. Предложения WHERE, за исключением самой таблицы перечислений, которая имеет максимум 64 строки. Поиск ваших дистрибьюторов для всех, кто отправляется в Индию и Калифорнию, по-прежнему может использовать сравнение равенства и индекс.

Теперь у вас есть просьба добавить поддержку для территорий США, коды почтовых отправлений вооруженных сил и провинций Канады и сделать это в обратном порядке. Там нет резки списка до < 64 записи, и бизнес действительно хочет избежать необходимости разделять страны старой школы с остальными территориями и подразделениями.

Чем вы занимаетесь?

Приветствуются творческие ответы, но реальная проблема заключается в следующем: существует ли способ заставить одну и ту же побитовую математику, которая работает с 64-битными значениями без знака, работать над подписанными, используя отрицательное пространство, чтобы превысить 64 возможных бита, как в С#, так и в SQL (2008)? Если это имеет значение, флаг моделируется, а не "реальный" флаг, поэтому технически не обязательно, чтобы это работало против перечня CLR с атрибутом [Flags].

  • 1
    Комбинации да, но в этом случае комбинация подобна кораблю в Индиану и Калифорнию, а не в один штат. В любом случае, может быть, вы могли бы попробовать использовать GUID? На самом деле это целые 128-битные числа.
  • 1
    Отрицательное пространство - это только один из 64 битов, так что нет.
Показать ещё 2 комментария
Теги:
bit-manipulation
flags

3 ответа

5
Лучший ответ

Вы не можете превышать 64 бит по 64-битовому значению, даже не используя "отрицательное пространство". Если у вас 64 бит, у вас есть 64 бит. Вы можете использовать Guid, чтобы получить 128 бит, что помешает проблеме на некоторое время, но в конечном итоге вам нужно будет добавить дополнительные поля.

3

В SQL Server вы можете попробовать десятичное (38,0)

Это дает вам 38 цифр слева от десятичной точки (1E38). В двоичных выражениях это около 126 бит (8.5E37). И его можно манипулировать как число.

Однако одним из вариантов было бы определить, что вы хотите в .NET, и использовать в SQL Server соответствующий тип данных CLR. Таким образом, это может быть согласовано между двумя платформами.

Однако, я бы действительно подумал о том, чтобы отказаться от флагов...

  • 0
    К сожалению, десятичный тип недопустим для побитовых операторов. Идея типа данных CLR интересна; Я выясню, смогу ли я установить> 64-битные флаги под этим углом. Что касается перехода от флагов, я обычно использую их только там, где, как я ожидаю, существует дюжина или меньше возможных значений, и нет никакой другой причины для создания отношения «многие ко многим». У меня также есть инструмент, который автоматически генерирует индексируемые запросы на стороне SQL и содержит для них операторы на стороне .NET, но я ограничиваю их использование случаями, когда они могут сэкономить как пространство, так и время кодирования.
1

Мой 2c: Ты пытаешься быть умным, и ты причиняешь себе боль. Битфилды и SQL не очень хорошо смешиваются, прежде всего потому, что битовые поля не могут быть правильно проиндексированы, и любой поиск должен будет выполнить полное сканирование. Например. чтобы найти всех реселлеров, отправляющихся в АК, вам нужно отсканировать всю таблицу реселлеров. Также решение не масштабируется до более чем 64 значений (как вы уже обнаружили). Это также плохой выбор хранилища, для этого требуется бит со значением 0 для хранения отрицательной информации (отсутствие связи).

Используйте отдельную таблицу для моделирования отношений "многие-ко-многим" между реселлерами и государствами/территориями/провинциями/странами, к которым они отправляются.

  • 0
    Дело принято. Что касается индексации, тем не менее, поиск не требует сканирования таблицы в таблице посредников. В 64-строковой таблице максимальных состояний выполняется одно сканирование, чтобы создать переменную таблицы indexed @ StateIDs, которая содержит выходные данные всех побитовых сравнений. (Эта работа на самом деле также может быть сохранена, но она не достаточно ресурсоемкая, чтобы беспокоиться об этом.) Таблица @StateIDs var затем по значению присоединяется к внешнему ключу таблицы посредников для получения совпадений без сканирования таблицы.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню