Sum Booleans and then display only over a certain count - libreoffice

I've been banging my head against the wall a while.
I have a table that has DwgID (integer) and Current (boolean) as part of the table.
What I want to do is filter just the DwgID's that have more than 1 Current selected.
This code gives me the count correctly:
`SELECT "DwgID", SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) "Current" FROM "tblRev" GROUP BY "DwgID"`
But if I try to add a criteria where the Current Count is > 1, it fails with a data content could not be loaded.
`SELECT "DwgID", SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) "Current" FROM "tblRev" GROUP BY "DwgID" HAVING ( ( SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) > 1 ) )`
What am I doing wrong?

You need to choose a different alias because Current is the column name. Let's call it NumCurrent instead.
SELECT "DwgID", SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) "NumCurrent" FROM "tblRev" GROUP BY "DwgID" HAVING ( ( SUM( CASE WHEN "Current" = 1 THEN 1 ELSE 0 END ) > 1 ) )

Related

Max(case when) to expand indicator from single row to group of rows

I need to expand the indicator (currently on daily basis) to a larger group (groups multiple consecutive days into one grp). I have following type of data:
id date grp new_ind traditional_ind
--------------------------------------------------
1 02-01-2021 1 1 0
1 02-02-2021 1 0 1
1 02-03-2021 1 0 0
1 02-04-2021 1 null null
1 02-06-2021 2 0 1
1 02-07-2021 2 0 0
2 02-01-2021 1 null null
where new_ind and traditional_ind are mutually exclusive. With this, I am trying to create new indicator that expands the indicators that are currently on daily level to grp level, that will look like:
id date grp new_ind traditional_ind final_type
----------------------------------------------------------------
1 02-01-2021 1 1 0 new
1 02-02-2021 1 0 1 new
1 02-03-2021 1 0 0 new
1 02-04-2021 1 null null new
1 02-06-2021 2 0 1 traditional
1 02-07-2021 2 0 0 traditional
2 02-01-2021 1 null null none
basically,
if new_ind was ever 1, I want to flag entire grp as 'new'
if new_ind=0 and if traditional_ind is ever 1, flag entire grp as 'traditional'
if both indicators were null, then flag entire grp as 'none'
so that each id and grp can have single value of final_type.
I've tried:
max(case when new_ind = 1 then 'New'
when traditional_ind = 1 then 'Traditional'
else 'None' end) over (partition by id, grp) as final_type
but this wouldn't recognize when new_ind=1 then 'New' and flag all of new_ind = 1 as 'None' (but show traditional correctly):
id date grp new_ind traditional_ind final_type
----------------------------------------------------------------
1 02-01-2021 1 1 0 none
1 02-02-2021 1 0 1 none
1 02-03-2021 1 0 0 none
1 02-04-2021 1 null null none
1 02-06-2021 2 0 1 traditional
1 02-07-2021 2 0 0 traditional
2 02-01-2021 1 null null none
But if I remove else statement and only run:
max(case when new_ind = 1 then 'New'
when traditional_ind = 1 then 'Traditional'
end) over (partition by id, grp) as final_type
then this does accurately expand indicator as I hope, just returns null values (which I need to show as 'None' instead of nulls):
id date grp new_ind traditional_ind final_type
----------------------------------------------------------------
1 02-01-2021 1 1 0 new
1 02-02-2021 1 0 1 new
1 02-03-2021 1 0 0 new
1 02-04-2021 1 null null new
1 02-06-2021 2 0 1 traditional
1 02-07-2021 2 0 0 traditional
2 02-01-2021 1 null null null
Can anyone help identify issue with my max case when statement?
I think something like this should work:
WITH final_types AS (
SELECT
id,
grp,
( case
when bool_or(new_ind = 1) then 'New'
when bool_or(traditional_ind = 1) then 'Traditional'
else 'None'
end
) AS final_type
FROM your_table
GROUP BY id, grp
)
SELECT
t1.*,
t2.final_type
FROM your_table t1
JOIN final_types t2 ON t1.id = t2.id AND t1.grp = t2.grp

Multiple Case Statements With Identical Expressions

I'm trying to evaluate a specific column to return five different columns - but the columns are based off the same expression in the CASE statements.
CASE WHEN va.HIN LIKE '%[-=!##$%^&*()<>?:|\;./,]%' THEN 1
ELSE 0
END AS [Invalid] ,
CASE WHEN va.HIN LIKE '%[-=!##$%^&*()<>?:|\;./,]%' THEN 0
ELSE 1
END AS [validMICcode] ,
CASE WHEN va.HIN LIKE '%[-=!##$%^&*()<>?:|\;./,]%' THEN 0
ELSE 1
END AS [validSerialNumber] ,
CASE WHEN va.HIN LIKE '%[-=!##$%^&*()<>?:|\;./,]%' THEN 0
ELSE 1
END AS [validFormat] ,
CASE WHEN va.HIN LIKE '%[-=!##$%^&*()<>?:|\;./,]%' THEN 0
ELSE 1
END AS [validProductionYear] ,
I feel like is causing the table / column in question to be searched for the pattern five times, but I cannot figure out how rewrite it - or if it is even possible - to have one pattern search and define the columns based on the one search.
I'm trying different variations, but I cannot come up with the correct syntax for this issue:
CASE WHEN va.HIN LIKE '%[-=!##$%^&*()<>?:|\;./,]%' THEN 1
ELSE 0
CASE WHEN 1 THEN 1 END AS [Invalid]
CASE WHEN 0 THEN 0 END AS [validMICode]
CASE WHEN 0 THEN 0 END AS AS [validSerialNumber]
CASE WHEN 0 THEN 0 END AS AS [validFormat]
CASE WHEN 0 THEN 0 END AS AS [validProductionYear]
END
One way would be a sub query and Bitwise NOT.
select
result Invalid,
~result validMICcode,
~result validSerialNumber,
~result validFormat,
~result validProductionYear
from
(
select
CASE WHEN va.HIN LIKE '%[-=!##$%^&*()<>?:|\;./,]%'
THEN CAST(1 As bit)
ELSE CAST(0 As bit)
End result
from ...
) tbl
You could do that with a CTE..
with cte as(
select *,
CASE
WHEN va.HIN LIKE '%[-=!##$%^&*()<>?:|\;./,]%'
THEN 1
ELSE 0
END as SomeColumn)
select *,
CASE WHEN SomeColumn = 1 THEN 1 END AS [Invalid]
CASE WHEN SomeColumn = 0 THEN 0 END AS [validMICode]
...
from cte

Find last occurring value within record in PostgreSQL

I'm not new to SQL, but I am new to PostgreSQL and am really struggling to adapt my current knowledge in a different environment.
I am trying to create a variable that captures whether or not someone stays active, skips, or churns within a 0/1 time series variable. For example, in the data below, my dataset would include the variables id,time, and voted, and I would create the variable "skipped":
id time voted skipped
1 1 1 active
1 2 0 skipped
1 3 1 active
2 1 1 active
2 2 0 churned
2 3 0 churned
3 1 1 active
3 2 1 active
3 3 0 churned
The rule for coding "skipped" is pretty simple: If 1 is the last record, the person is "active" and any zeroes count as "skipped", but if 0 is the last record, the person is "churned".
The record with id = 1 is a skip because id is non-zero at time 3 after being 0 at time 2. The other two cases, 0 is the final value so they are "churned". Can anyone help? I've been noodling on it all day, and am hitting a wall.
This isn't particularly elegant, but it should meet your needs:
with votes as (
select
id, time, voted,
max(time) over (partition by id) as max_time
from voter_data
)
select
v1.id, v1.time, v1.voted,
case
when v1.voted = 1 then 'active'
when v2.voted = 1 then 'skipped'
else 'churned'
end as skipped
from
votes v1
join votes v2 on
v1.id = v2.id and
v1.max_time = v2.time
In a nutshell, we first figure out which is the last record for each voter id, and then we do a self-join on the resulting table to isolate only that last id.
There is a chance this could produce multiple results -- if it's possible to have the same ID vote twice at the same time. If that's the case, you want row_number() instead of max().
Results on your data:
1 1 1 'active'
1 2 0 'skipped'
1 3 1 'active'
2 1 1 'active'
2 2 0 'churned'
2 3 0 'churned'
3 1 1 'active'
3 2 1 'active'
3 3 0 'churned'
Window functions can help for readability when working with self-referential joins.
WITH
add_last_voted_status AS (
SELECT
*
, LAST_VALUE(voted) OVER (
PARTITION BY id
ORDER BY time
) AS last_voted_status
FROM table
)
SELECT
id
, time
, voted
, CASE
WHEN last_voted_status = 0
THEN 'churned'
WHEN last_voted_status = 1 AND voted = 1
THEN 'active'
WHEN last_voted_status = 1 AND voted = 0
THEN 'skipped'
ELSE '?'
END AS skipped
FROM add_last_voted_status

Count valid values per user

I have a table with a list of values. -1 is a blank value:
ID FieldType1A FieldType1B FieldType2A FieldType2B Person
1 15 14 10 -1 1
2 16 -1 12 10 1
3 17 -1 5 6 1
4 6 -1 7 -1 2
...
So the result should be:
Person FieldType1 FieldType2
1 4 5
2 1 1
there is a users table with a list of user IDs, would there be a way of iterating over that list of values to generate the person list in the result set (0 for the field types being perfectly valid as it is merely counts)? I think the answer to T-SQL Column Values Count is a step in the direction I'm attempting to go, but unsure how to combine columns that are the same (the A/Bs allow for a list of answers). That and I'm interested in combining all valid values as not attempting to count the number of each valid response.
You can use a CASE expression to change all non-negative-one values to 1, and -1 values to 0, and then sum them up.
SELECT Person,
SUM(CASE WHEN FieldType1A <> -1 THEN 1 ELSE 0 END) +
SUM(CASE WHEN FieldType1B <> -1 THEN 1 ELSE 0 END) AS FieldType1,
SUM(CASE WHEN FieldType2A <> -1 THEN 1 ELSE 0 END) +
SUM(CASE WHEN FieldType2B <> -1 THEN 1 ELSE 0 END) AS FieldType2
FROM YourTable
GROUP BY Person
SELECT Person,
count(nullif(FieldType1A, -1)) + count(nullif(FieldType1B, -1)) as FieldType1,
count(nullif(FieldType2A, -1)) + count(nullif(FieldType2B, -1)) as FieldType2
FROM yourtable
GROUP BY person

Changing position of data in columns depending on actual data

Suppose I have a table like below
ID Marks1 Marks2 Marks3
-------------------------
1 10 0 4
2 0 40 90
Now, I need to select from this table in a way that will give precedence to positive values first. So if the marks are 0 then it will be shifted to right. The SELECT should give following output
ID Marks1 Marks2 Marks3
-------------------------
1 10 4 0
2 40 90 0
Can you please guide me for the approach? It will be great if it can be done in a select statement itself. Thanks in advance.
Something like this you will need to check for each subsequent row that the previous column isn't 0. Have selected the values out as null as it makes the code slightly easier to read as i can use coalesce
Select
Coalesce(Marks1, Marks2, Marks3,0) as Marks1,
Case when marks1 is not null
then Coalesce(Marks2, Marks3, 0) else 0
end as Marks2,
case when marks1 is not null
and marks2 is not null
then Coalesce(Marks3,0)
end as Marks3
from
(
Select
Case when Marks1 =0 then null else Marks1 end as Marks1,
Case when Marks2 =0 then null else Marks2 end as Marks2,
Case when Marks3 =0 then null else Marks3 end as Marks3
From mytbl
)