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

Возьмем такой сценарий: у вас есть несколько перечислений флагов в C#, связанных (и фактически сгенерированных) с таблицами перечисления в 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, так и в C#, используя побитовую логику, которая позволяет мгновенно гидрировать списки флажков и выбирать списки в Asp.NET, сохраняя при этом только одно 64-разрядное число для хранения любой комбинации выборов. И вам не нужен неиндексируемый оператор сравнения в предложениях WHERE ваших процедур, за исключением самой таблицы enum, которая имеет максимум 64 строки. Поиск в ваших дистрибьюторах всех, кто отправляет товары в Индиану и Калифорнию, все еще может использовать сравнение на равенство и индекс.

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

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

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

3 ответа

Решение

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

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

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

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

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

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

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

Другие вопросы по тегам