Click here to Skip to main content
15,891,033 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

I'm working on a project where efficiency of the search functionality is critical.

I have several flag columns (like enum flags in c#). Searching on this data is super fast (3 milliseconds round trip) but I've come-a-cropper now I have to perform group counts.

So, I have an item 'A' that contains Red (1), White (8) and blue (64) so the 'Colours' column holds the number 73.

To search I can search for items with red with this
SQL
Declare @colour int
set @colour = 1
Select * from Items where (Colour & @colour) > 0


That works great. Now I have to group it (also super fast)

so if I have 8 items in total, 5 contain red, 3 contain white and 7 contain blue the results would look like:
Colour      Qty
1           5
8           3
64          7
( I don't have to worry about the name )

So: Is there any way I can take the number 73 and bitwise split it into groups?

(Part 2: How do I translate that into Linq to SQL?)

Any advise would be appreciated
Thanks ^_^

What I have tried:

Tried writing the login in Linq but forgot that it would have no translation into TSQL.

I'm thinking about scrapping this column and turning it back into a linking table, but I really don't want to have to redesign this thing for a third time >_<
Posted
Updated 25-Jun-18 0:56am

SQL
SELECT VAL, COUNT(*)
FROM (
	SELECT 
		COLOR AS COLOR_VAL, 
		COLOR & 1 AS [1],
		COLOR & 2 AS [2],
		COLOR & 4 AS [4],
		COLOR & 8 AS [8],
		COLOR & 16 AS [16],
		COLOR & 32 AS [32],
		COLOR & 64 AS [64],
		COLOR & 128 AS [128]
	FROM (SELECT 73 AS COLOR UNION SELECT 53 UNION SELECT 91) AS COLORS) AS SUMMARY
UNPIVOT  
   (VAL FOR COLOR IN   
      ([1], [2], [4], [8], [16], [32], [64], [128])  
) AS UP
WHERE VAL > 0 
GROUP BY VAL


The bold part is where you actual select for your color values comes in...
 
Share this answer
 
Comments
Andy Lanng 26-Jun-18 4:05am    
ooh - thing is: the number of options (flags) is anywhere between 10 and 60 (hence bigints). Does this still stand up for performance pivoting so many columns?
Kornfeld Eliyahu Peter 26-Jun-18 4:09am    
As long as it is a fixed number of values... You should put it into a SP, so it will be pre-compiled and that will give you good performance...
Ok - I think I've worked out the best solution:

I tries a view with a cte:
SQL
with cte as (
	select cast(1 as bigint) as flag, 1 pow
	union all
	select POWER(cast(2 as bigint),pow), pow + 1
	from cte
	where flag < POWER(cast(2 as bigint),62)
)
, cte2 as (
	select flag from cte
	union select -9223372036854775808
)


but that was too slow so now I have made it into a static table. I join with a bitwise '&':
SQL
select Flag, Count(*)
From FlagValues fv 
inner join Items i on (fv.Flag & i.Colour)


Much faster ^_^
 
Share this answer
 
Comments
Richard Deeming 26-Jun-18 12:11pm    
Depending on the data and the indexes, you might get slightly better performance with:
INNER JOIN Items i ON fv.Flag <= i.Colour And (fv.Flag & i.Colour) = fv.Flag

SQL can't index the & operation, but it should be able to use an index on the Colour column to satisfy the <= join.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900