Postgres bitmask group by - postgresql

I have the following flags declared:
0 - None
1 - Read
2 - Write
4 - View
I want to write a query that will group on this bitmask and get the count of each flag used.
person mask
a 0
b 3
c 7
d 6
The result should be:
flag count
none 1
read 2
write 3
view 2
Any tips would be appreciated.
For Craig
SELECT lea.mask as trackerStatusMask,
count(*) as count
FROM Live le
INNER JOIN (
... --some guff
) lea on le.xId = lea.xId
WHERE le.xId = p_xId
GROUP BY lea.mask;

SQL Fiddle
select
count(mask = 0 or null) as "None",
count(mask & 1 > 0 or null) as "Read",
count(mask & 2 > 0 or null) as "Write",
count(mask & 4 > 0 or null) as "View"
from t

Simplest - pivoted result
Here's how I'd approach it:
-- (after fixing the idiotic mistakes in the first version)
SELECT
count(nullif(mask <> 0, True)) AS "none",
count(nullif(mask & 2,0)) AS "write",
count(nullif(mask & 1,0)) AS "read",
count(nullif(mask & 4,0)) AS "view"
FROM my_table;
-- ... though #ClodAldo's version of it below is considerably clearer, per comments.
This doesn't do a GROUP BY as such; instead it scans the table and collects the data in a single pass, producing column-oriented results.
If you need it in row form you can pivot the result, either using the crosstab function from the tablefunc module or by hand.
If you really must GROUP BY, explode the bitmask
You cannot use GROUP BY for this in a simple way, because it expects rows to fall into exactly one group. Your rows appear in multiple groups. If you must use GROUP BY you will have to do so by generating an "exploded" bitmask where one input row gets copied to produce multiple output rows. This can be done with a LATERAL function invocation in 9.3, or with a SRF-in-SELECT in 9.2, or by simply doing a join on a VALUES clause:
SELECT
CASE
WHEN mask_bit = 1 THEN 'read'
WHEN mask_bit = 2 THEN 'write'
WHEN mask_bit = 4 THEN 'view'
WHEN mask_bit IS NULL THEN 'none'
END AS "flag",
count(person) AS "count"
FROM t
LEFT OUTER JOIN (
VALUES (4),(2),(1)
) mask_bits(mask_bit)
ON (mask & mask_bit = mask_bit)
GROUP BY mask_bit;
I don't think you'll have much luck making this as efficient as a single table scan, though.

Related

comparison within in clause of postgresql

Is it possible to add condition within the in clause of postgresql
for example
select ... where (t1.subject,t2.weight) in ((1,2),(2,3))
I want to check whether subject is 1 but weight can be >= 2 not just 2 and so on. So that condition would logically look somewhat like
select ... where (t1.subject,t2.weight) in ((1,>2),(2,>3))
No, this is not possible. You need to write
…
WHERE t1.subject = 1 AND t2.weight > 2
OR t1.subject = 2 AND t2.weight > 3;
You can select value of object using subquery. Simple just select query subject which are having weight greater than >=2.
select ... where (t1.subject,t2.weight) in (select subject FROM ... where weight >=2 ,select subject FROM ... where weight >=3 );

Query table by a value in the second dimension of a two dimensional array column

WHAT I HAVE
I have a table with the following definition:
CREATE TABLE "Highlights"
(
id uuid,
chunks numeric[][]
)
WHAT I NEED TO DO
I need to query the data in the table using the following predicate:
... WHERE id = 'some uuid' and chunks[????????][1] > 10 chunks[????????][3] < 20
What should I put instead of [????????] in order to scan all items in the first dimension of the array?
Notes
I'm not entirely sure that chunks[][1] even close to something I need.
All I need is to test a row, whether its chunks column contains a two dimensional array, that has in any of its tuples some specific values.
May be there's better alternative, but this might do - you just go over first dimension of each array and testing your condition:
select *
from highlights as h
where
exists (
select
from generate_series(1, array_length(h.chunks, 1)) as tt(i)
where
-- your condition goes here
h.chunks[tt.i][1] > 10 and h.chunks[tt.i][3] < 20
)
db<>fiddle demo
update as #arie-r pointed out, it'd be better to use generate_subscripts function:
select *
from highlights as h
where
exists (
select *
from generate_subscripts(h.chunks, 1) as tt(i)
where
h.chunks[tt.i][3] = 6
)
db<>fiddle demo

DB2 subquery not working using IN statement SQLCODE 115

I'm trying to execute a query in DB2. But it throws following error:
Error: DB2 SQL Error: SQLCODE=-115, SQLSTATE=42601, SQLERRMC=IN, DRIVER=4.8.86
SQLState: 42601
ErrorCode: -115
Error: DB2 SQL Error: SQLCODE=-514, SQLSTATE=26501, SQLERRMC=SQL_CURSH200C1; STMT0001, DRIVER=4.8.86
SQLState: 26501
ErrorCode: -514
Which does'nt make sense as my query looks correct:
SELECT ROW_NUMBER() OVER() AS ID,
CONCAT(TRIM(TB1.ROW1),CONCAT('_',TRIM(TB1.ROW2))) AS CODE_DESCRIPTION,
CASE
WHEN TRIM(TB1.ROW1) IN (SELECT T1.ROW1 FROM DB1.TABLE1 T1 WHERE T1.ROW3 = 'TEST')
THEN 'Valid'
ELSE 'Invalid'
END,
TB1.* FROM DB1.TABLE1 TB1
WHERE TB1.ROW3 = 'CLASS1';
SQLCode 115 means Comparison is invalid. Which is not ?
Update:
What I'm trying to accomplish here is. I have a Table Table1(Name changed for simplicity). Following is the part of the content.
**Row3** **Row1** **Row2**
KSASPREM SRQ 0 0 Auto Carry SRQ
KSASPREM SCG 0 0 BRT Buses SCG
KSASPREM SCE 0 0 Buses SCE
KSASPREM SRR 0 0 Buses SRR
KSASPREM SDC 0 0 Domestic All Risks SDC
KSASPREM SDA 0 0 Domestic Buildings SDA
Task to accomplish:
Retrieve all the values from Table1 where Row3 is KSASPREM
The result should contain one extra column 'Valid' value Yes/No if value of Row1 is not in the Values retrieved from Table1 where Row3 is 'TEST'
Hope I made myself clear and not more confusing ?
Any Help ?
Thanks
Ps. Updated the Query
As with so many things, a JOIN (here, LEFT JOIN) is the answer. Specifically, we need to put the (slightly modified) subquery as the table reference:
LEFT JOIN (SELECT DISTINCT row1, 'Valid' as valid
FROM Table1
WHERE row3 = 'TEST') AS Test
ON Test.row1 = TB1.row1
LEFT JOIN tells the query engine that "rows in this other table aren't required".
DISTINCT says, "for all value combinations in these columns, give me just one row"
Using a constant value - 'Valid' - returns that constant value.
... so this gets us a (virtual, temp) table containing unique row1 entries where row3 = 'test'.
Here's the full query:
SELECT ROW_NUMBER() OVER(ORDER BY TB1.row1) AS ID,
TRIM(TB1.ROW1) || '_' || TRIM(TB1.ROW2) AS CODE_DESCRIPTION,
COALESCE(Test.valid, 'Invalid') AS valid,
TB1.row3, TB1.row1, TB1.row2
FROM Table1 TB1
LEFT JOIN (SELECT DISTINCT row1, 'Valid' as valid
FROM Table1
WHERE row3 = 'TEST') Test
ON Test.row1 = TB1 .row1
WHERE TB1.ROW3 = 'KSASPREM'
SQL Fiddle Example
COALESCE(...) returns the first non-null value encountered in the value list. Since, if there is no Test row, Test.valid will be null, this outputs 'Invalid' for TB1 rows without a corresponding Test row. (Internally it's calling CASE, I believe, this just makes it prettier)
Note that:
I've put an ORDER BY into the OVER clause, to return (mostly) consistent results. If you only ever plan on running this once it doesn't matter, but if you need to run it multiple times and get consistent IDs, you'll need to use something that won't be shuffled.
DB2 (and apparently PostgreSQL) support || as a concat operator. It makes reading statements so much easier to understand.
Never use SELECT *, it isn't safe for several reasons. Always specify which columns you want.

Adding calculation in where condition

I have the following code
Select MR.MRPRoductId,MR.PartNo, E.MaterialName as Producttomake,EP.MaterialName as NeededProduct,(MR.ApprovedQty*PCM.NeededQuantity-isnull(SUM(ISS.IssuedQty),0))as Needed,PCM.NeededProductPartNo,
EP.UOM,ISNULL(SM.Quantity,0)as Available from Material_Requisition MR
join Productconversion_master PCM on MR.PartNo=PCM.ConvertedProductPartNo
Left join ProductConversion_IssueSlip ISS on PCM.NeededProductPartNo=ISS.IssuedPartNo
join ElectricalProducts E on MR.PartNo=E.PartNo join ElectricalProducts EP on PCM.NeededProductPartNo=EP.PartNo
Left join StockMaster SM on PCM.NeededProductPartNo=SM.PartNo and SM.LocationId='LOC_001' where MR.MRID='TFIPL/MR/CON/NR/2013-14/00001'
group by EP.MaterialName,MR.MRPRoductId,MR.PartNo,E.MaterialName,
MR.ApprovedQty,PCM.NeededQuantity,PCM.NeededProductPartNo,EP.UOM,SM.Quantity
the result of this query as follows
i want to select only Needed>0 .how to add it in where condition
Two options.
Option 1 - simplest in my opinion
SELECT list
, of
, columns
FROM (
<your query goes here>
) As a_subquery
WHERE needed > 0
Option 2
<your_query goes here>
HAVING (MR.ApprovedQty*PCM.NeededQuantity-isnull(SUM(ISS.IssuedQty),0)) > 0
You can't use a WHERE condition in this scenario because it is applying to an aggregate. The equivalent to this for aggregates is the HAVING clause (which is placed after GROUP BY)

sp_executesql vs user defined scalar function

In the table below I am storing some conditions like this:
Then, generally, in second table, I am having the following records:
and what I need is to compare these values using the right condition and store the result ( let's say '0' for false, and '1' for true in additional column).
I am going to do this in a store procedure and basically I am going to compare from several to hundreds of records.
What of the possible solution is to use sp_executesql for each row building dynamic statements and the other is to create my own scalar function and to call it for eacy row using cross apply.
Could anyone tell which is the more efficient way?
Note: I know that the best way to answer this is to make the two solutions and test, but I am hoping that there might be answered of this, based on other stuff like caching and SQL internal optimizations and others, which will save me a lot of time because this is only part of a bigger problem.
I don't see the need in use of sp_executesql in this case. You can obtain result for all records at once in a single statement:
select Result = case
when ct.Abbreviation='=' and t.ValueOne=t.ValueTwo then 1
when ct.Abbreviation='>' and t.ValueOne>t.ValueTwo then 1
when ct.Abbreviation='>=' and t.ValueOne>=t.ValueTwo then 1
when ct.Abbreviation='<=' and t.ValueOne<=t.ValueTwo then 1
when ct.Abbreviation='<>' and t.ValueOne<>t.ValueTwo then 1
when ct.Abbreviation='<' and t.ValueOne<t.ValueTwo then 1
else 0 end
from YourTable t
join ConditionType ct on ct.ID = t.ConditionTypeID
and update additional column with something like:
;with cte as (
select t.AdditionalColumn, Result = case
when ct.Abbreviation='=' and t.ValueOne=t.ValueTwo then 1
when ct.Abbreviation='>' and t.ValueOne>t.ValueTwo then 1
when ct.Abbreviation='>=' and t.ValueOne>=t.ValueTwo then 1
when ct.Abbreviation='<=' and t.ValueOne<=t.ValueTwo then 1
when ct.Abbreviation='<>' and t.ValueOne<>t.ValueTwo then 1
when ct.Abbreviation='<' and t.ValueOne<t.ValueTwo then 1
else 0 end
from YourTable t
join ConditionType ct on ct.ID = t.ConditionTypeID
)
update cte
set AdditionalColumn = Result
If above logic is supposed to be applied in many places, not just over one table, then yes you may think about function. Though I would used rather inline table-valued function (not scalar), because of there is overhead imposed with use of user defined scalar functions (to call and return, and the more rows to be processed the more time wastes).
create function ftComparison
(
#v1 float,
#v2 float,
#cType int
)
returns table
as return
select
Result = case
when ct.Abbreviation='=' and #v1=#v2 then 1
when ct.Abbreviation='>' and #v1>#v2 then 1
when ct.Abbreviation='>=' and #v1>=#v2 then 1
when ct.Abbreviation='<=' and #v1<=#v2 then 1
when ct.Abbreviation='<>' and #v1<>#v2 then 1
when ct.Abbreviation='<' and #v1<#v2 then 1
else 0
end
from ConditionType ct
where ct.ID = #cType
which can be applied then as:
select f.Result
from YourTable t
cross apply ftComparison(ValueOne, ValueTwo, t.ConditionTypeID) f
or
select f.Result
from YourAnotherTable t
cross apply ftComparison(SomeValueColumn, SomeOtherValueColumn, #someConditionType) f