How to get the max count of coherently cells? - sql

I have following data in my table:
ID - Data
1 - 0
2 - 10
3 - 100
4 - 60
5 - 0
6 - 0
7 - 15
8 - 100
9 - 100
10 - 70
11 - 10
12 - 0
13 - 0
What I want is the max count of rows > 0.
This data shows the energy using of a device in percent of a given total.
What I basicly want to know is: how long is the device "on" the longest timeperiod?

I guess you are expecting result 5 (longest sequence of non-zero numbers) for your example data. You can do that with recursive CTE in Oracle:
with testData as (
select '1' id, '0' val from dual
union all
select '2', '10' from dual
union all
select '3', '100' from dual
union all
select '4', '60' from dual
union all
select '5', '0' from dual
union all
select '6', '0' from dual
union all
select '7', '15' from dual
union all
select '8', '100' from dual
union all
select '9', '100' from dual
union all
select '10', '70' from dual
union all
select '11', '10' from dual
union all
select '12', '0' from dual
union all
select '13', '0' from dual
) ,
cte (id, val, sumX) as (
select t.id, val, case when val !=0 then 1 else 0 end from testData t where id = 1
union all
select t.id, t.val, case when t.val !=0 then sumX+1 else 0 end
from testData t
inner join cte
on cte.id = t.id-1
)
select max(sumx) from cte

Related

SQL, case statement not working. The error states that the field POS_Nbr is not aggregated or grouped. Any advice?

This is the sql code, the error is under the format line.
SELECT
Store,
FORMAT("%.0f%%",
CASE WHEN POS_Nbr in(60,61,62,63)
THEN (COUNT(DISTINCT POS_Unique_ID)/COUNT (Cal_Day))
ELSE 0 END) as percent_pure_ACO_mode_at_front_end,
FROM `ca-pr-cda-views.SALES.POS_SALES_ALL`
GROUP BY Store
ORDER BY Store;
The problem is within your CASE expresssion - in the below code your construct is commented out (as it errors) and there is a different construct of aggregation with CASE expression...
WITH
tbl AS
(
Select 1 "STORE_ID", 60 "POS_NBR", 'B' "POS_ID", SYSDATE-1 "CAL_DAY" From Dual Union All
Select 1 "STORE_ID", 10 "POS_NBR", 'B' "POS_ID", SYSDATE-2 "CAL_DAY" From Dual Union All
Select 1 "STORE_ID", 20 "POS_NBR", 'B' "POS_ID", SYSDATE-3 "CAL_DAY" From Dual Union All
Select 2 "STORE_ID", 61 "POS_NBR", 'A' "POS_ID", SYSDATE "CAL_DAY" From Dual Union All
Select 2 "STORE_ID", 62 "POS_NBR", 'A' "POS_ID", SYSDATE-1 "CAL_DAY" From Dual Union All
Select 1 "STORE_ID", 63 "POS_NBR", 'A' "POS_ID", SYSDATE-2 "CAL_DAY" From Dual Union All
Select 1 "STORE_ID", 60 "POS_NBR", 'B' "POS_ID", SYSDATE-3 "CAL_DAY" From Dual
)
SELECT
STORE_ID "STORE_ID",
-- CASE WHEN POS_Nbr in(60,61,62,63) THEN (COUNT(DISTINCT POS_Unique_ID)/COUNT (Cal_Day)) ELSE 0 END "PCT"
Count(DISTINCT CASE WHEN POS_NBR Between 60 And 63 THEN POS_ID ELSE '0' END) / Count(CAL_DAY) "PCT"
FROM
tbl
GROUP BY STORE_ID
ORDER BY STORE_ID
/* R e s u l t
STORE_ID PCT
---------- ----------
1 .6
2 .5
*/
You should adjust it to your needs and datatypes buut the problem with group by will dissappear if you aggregate values returned by CASE expresion instead of trying to select CASE expression of aggregated values. Regards...

How to only pull example of a value when more than 50% of the results match

Code
Error
Warning
ABC
1
0
ABC
0
0
ABC
0
0
DEF
1
0
DEF
0
0
DEF
1
0
GHI
1
0
GHI
0
1
I need to be able to pull one value indicating that more than 50% of the values listed have a "1" in either the "error" or "warning" columns.
In this example, I would need to get back something like this:
Code
DEF
GHI
Since both "DEF" and "GHI" had more than 50% of their total rows with a "1" in either the "Error" or "Warning" column. "ABC" would not be included since it did not meet the criteria of 50%.
If the values of error and warning are always either 1 or 0 your query can simply be:
select code
from main
group by code
having avg(error) >= .5
or avg(warning) >= .5;
See it working here: https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=02cdf03753da82fe2d3686523165ff09
You can use:
SELECT code
FROM table_name
GROUP BY code
HAVING AVG(GREATEST(Error, Warning)) >= 0.5;
Which, for the sample data:
CREATE TABLE table_name (Code, Error, Warning) AS
SELECT 'ABC', 1, 0 FROM DUAL UNION ALL
SELECT 'ABC', 0, 0 FROM DUAL UNION ALL
SELECT 'ABC', 0, 0 FROM DUAL UNION ALL
SELECT 'DEF', 1, 0 FROM DUAL UNION ALL
SELECT 'DEF', 0, 0 FROM DUAL UNION ALL
SELECT 'DEF', 1, 0 FROM DUAL UNION ALL
SELECT 'GHI', 1, 0 FROM DUAL UNION ALL
SELECT 'GHI', 0, 1 FROM DUAL UNION ALL
SELECT 'JKL', 1, 0 FROM DUAL UNION ALL
SELECT 'JKL', 0, 0 FROM DUAL UNION ALL
SELECT 'JKL', 0, 1 FROM DUAL UNION ALL
SELECT 'JKL', 0, 0 FROM DUAL;
Outputs:
CODE
DEF
GHI
JKL
db<>fiddle here
with main as(
select 'ABC' as code, 1 as error, 0 as warning from dual
union all
select 'ABC' as code, 0 as error, 0 as warning from dual
union all
select 'ABC' as code, 0 as error, 0 as warning from dual
union all
select 'DEF' as code, 1 as error, 0 as warning from dual
union all
select 'DEF' as code, 0 as error, 0 as warning from dual
union all
select 'DEF' as code, 1 as error, 0 as warning from dual
union all
select 'GHI' as code, 1 as error, 0 as warning from dual
union all
select 'GHI' as code, 0 as error, 1 as warning from dual)
select
x.*,
case
when (code_error/code_total + code_warning/code_total) > 1/2 then 'show'
else 'hide'
end as isVisible
from(
select distinct code,
(select count(1) from main where code = x.code and error = 1) as code_error,
(select count(1) from main where code = x.code and warning = 1) as code_warning,
(select count(1) from main where code = x.code) as code_total
from main x)x
This example works for any tables on DB:
with table1 as
(
select 1 as id, 'ABC' as code, 1 as error, 0 as warning from dual
union all
select 2 as id, 'ABC' as code, 0 as error, 0 as warning from dual
union all
select 3 as id, 'ABC' as code, 0 as error, 0 as warning from dual
union all
select 4 as id, 'DEF' as code, 1 as error, 0 as warning from dual
union all
select 5 as id, 'DEF' as code, 0 as error, 0 as warning from dual
union all
select 6 as id, 'DEF' as code, 1 as error, 0 as warning from dual
union all
select 7 as id, 'GHI' as code, 1 as error, 0 as warning from dual
union all
select 8 as id, 'GHI' as code, 0 as error, 1 as warning from dual
)
select
mm.*
from (
select
t1.*,
count(1) over () as rowcount,
ROW_NUMBER() OVER(
ORDER BY id
) as rnum
from table1 t1
) mm
where mm.rnum > (mm.rowcount/2)

ORACLE SQL | If a column contains a value, then it will exclude a different value from the same column

I have this query that returns the data below it
select LISTAGG(d.DOCUMENT_TYPE_CD, ',') WITHIN GROUP (ORDER BY D.DOCUMENT_TYPE_CD) as value
from test_table d;
VALUE
---------
CI,ECI,POA
now I'm trying to add a condition whenever 'ECI' value is present, it should exclude 'CI' in the result like this one below
VALUE
---------
ECI,POA
I tried using case statement in where condition it prompted an error
select LISTAGG(d.DOCUMENT_TYPE_CD, ',')
WITHIN GROUP (ORDER BY D.DOCUMENT_TYPE_CD) as value
from test_table d
where CASE d.DOCUMENT_TYPE_CD
WHEN 'ECI' THEN d.DOCUMENT_TYPE_CD <> 'CI'
END;
ORA-00905: missing keyword
00905. 00000 - "missing keyword"
*Cause:
*Action:
Error at Line: 7 Column: 36
is there any other way I could resolve this?
See if this helps; read comments within code.
SQL> with
2 test (id, document_type_cd) as
3 -- sample data
4 (select 1, 'ECI' from dual union all
5 select 1, 'CI' from dual union all
6 select 1, 'POA' from dual union all
7 --
8 select 2, 'CI' from dual union all
9 select 2, 'POA' from dual union all
10 --
11 select 3, 'XYZ' from dual union all
12 select 3, 'ABC' from dual
13 ),
14 temp as
15 -- see whether CI and ECI exist per each ID
16 (select id,
17 sum(case when document_type_cd = 'CI' then 1 else 0 end) sum_ci,
18 sum(case when document_type_cd = 'ECI' then 1 else 0 end) sum_eci
19 from test
20 group by id
21 ),
22 excl as
23 -- exclude CI rows if ECI exist for that ID
24 (select a.id,
25 a.document_type_cd
26 from test a join temp b on a.id = b.id
27 where a.document_type_cd <> case when b.sum_ci > 0 and b.sum_eci > 0 then 'CI'
28 else '-1'
29 end
30 )
31 -- finally:
32 select e.id,
33 listagg(e.document_type_cd, ',') within group (order by e.document_type_cd) result
34 from excl e
35 group by e.id;
ID RESULT
---------- --------------------
1 ECI,POA
2 CI,POA
3 ABC,XYZ
SQL>
Something like this:
select LISTAGG(d.DOCUMENT_TYPE_CD, ',')
WITHIN GROUP (ORDER BY D.DOCUMENT_TYPE_CD) as value
from test_table d,
(select sum (case when DOCUMENT_TYPE_CD = 'CI' then 1 else 0 end) C
from test_table) A
where d.DOCUMENT_TYPE_CD <> case when A.c > 0 then 'CI' when A.c = 0 then ' ' end;
DEMO
You may identify the presence of both the values with two conditional aggregations in the same group by and then replace CI inside the result of listagg in one pass.
with a(id, cd) as (
select 1, 'ABC' from dual union all
select 1, 'ECI' from dual union all
select 1, 'CI' from dual union all
select 1, 'POA' from dual union all
select 2, 'XYZ' from dual union all
select 2, 'ECI' from dual union all
select 2, 'CI' from dual union all
select 2, 'POA' from dual union all
select 3, 'CI' from dual union all
select 3, 'POA' from dual union all
select 4, 'ABC' from dual union all
select 4, 'DEF' from dual
)
select
id,
ltrim(
/*Added comma in case CI will be at the beginning*/
replace(
',' || listagg(cd, ',') within group (order by cd asc),
decode(
/*If both are present, then replace CI. If not, then do not replace anything*/
max(decode(cd, 'CI', 1))*max(decode(cd, 'ECI', 1)),
1,
',CI,'
),
','
),
','
) as res
from a
group by id
ID | RES
-: | :----------
1 | ABC,ECI,POA
2 | ECI,POA,XYZ
3 | CI,POA
4 | ABC,DEF
db<>fiddle here
Instead of using GROUP BY, you can also use windowing (aka analytic) functions to check the presence of ECI per group (test data shamelessly stolen from #littlefoot):
with
test (id, document_type_cd) as
-- sample data
(select 1, 'ECI' from dual union all
select 1, 'CI' from dual union all
select 1, 'POA' from dual union all
--
select 2, 'CI' from dual union all
select 2, 'POA' from dual union all
--
select 3, 'XYZ' from dual union all
select 3, 'ABC' from dual
),
temp as
(select id,
document_type_cd,
sum(case when document_type_cd = 'ECI' then 1 else 0 end) over (partition by id) as sum_eci
from test
)
select a.id,
listagg(a.document_type_cd, ',') within group (order by a.document_type_cd) result
from temp a
where a.document_type_cd != 'CI' or sum_eci = 0
group by a.id;

Oracle Between return is not expected

I have a varchar colunm. with content
ColumnX
________
ABC
DEF
1
2
3
4
40
50
I need to get number between 1 and 4. SO i have this SQL
SELECT columnX
FROM table
WHERE regexp_substr(columnX, '[[:digit:]]') BETWEEN 1 and 4;
But my result i get is 1,2,3,4 and 40.
What shall I do to get it right?
You have to use a quantifier to match more than 1 character. In this case, I used the + quantifier, which matches one or more occurrences of the characters (digits, in this case).
Try this:
WITH
test_data AS
(SELECT 'ABC' AS columnX FROM dual
UNION ALL SELECT '1' FROM dual
UNION ALL SELECT '2' FROM dual
UNION ALL SELECT '3' FROM dual
UNION ALL SELECT '4' FROM dual
UNION ALL SELECT '40' FROM dual
UNION ALL SELECT '50' FROM dual
)
SELECT columnX
FROM test_data
WHERE regexp_substr(columnX, '[[:digit:]]+') BETWEEN 1 AND 4;
Output:
COLUMNX
-------
1
2
3
4

How to find a specific pattern in some Access data?

I have ex 17 rows and 2 columns in my tabel. Like this:
ColA ColB
---- ----
X 1
X 2
X 3
X a
Y 1
Y 2
Y a
Z 4
Z 4
Z b
Q 1
Q 2
Q 3
Q a
W 4
W b
W 5
Is there a way to look for a pattern in colB of 1,2,3,a for the same value of ColA?
That would give me at output of X and Q.
Your sample data shows distinct rows. In that case, you can use this GROUP BY query.
SELECT y.ColA
FROM YourTable AS y
WHERE y.ColB In ('1','2','3','a')
GROUP BY y.ColA
HAVING Count(*) = 4;
If your actual data might include duplicate rows, you can start with SELECT DISTINCT in a subquery before applying the GROUP BY.
SELECT sub.ColA
FROM
(
SELECT DISTINCT y.ColA, y.ColB
FROM YourTable AS y
WHERE y.ColB In ('1','2','3','a')
) AS sub
GROUP BY sub.ColA
HAVING Count(*) = 4;
(I've assumed that your table is named [PatternData].)
If you use Allen Browne's ConcatRelated function you can create a query to "string together" all of the [ColB] values for each distinct value of [ColA] like this...
SELECT
ColA,
ConcatRelated("ColB", "PatternData", "ColA=""" & ColA & """" , "ColB", "") AS ColB_values
FROM (SELECT DISTINCT ColA FROM PatternData)
...returning...
ColA ColB_values
---- -----------
Q 123a
W 45b
X 123a
Y 12a
Z 44b
Then you can use the above query as the basis for a query to find the [ColA] values with the desired pattern
SELECT ColA
FROM
(
SELECT
ColA,
ConcatRelated("ColB", "PatternData", "ColA=""" & ColA & """" , "ColB", "") AS ColB_values
FROM (SELECT DISTINCT ColA FROM PatternData)
)
WHERE ColB_values = "123a"
...returning...
ColA
----
Q
X
Below is one possible solution:
WITH
data AS (
SELECT 'X' cola, '1' colb FROM dual
UNION ALL SELECT 'X' cola, '2' FROM dual
UNION ALL SELECT 'X', '3' FROM dual
UNION ALL SELECT 'X', 'a' FROM dual
UNION ALL SELECT 'Y', '1' FROM dual
UNION ALL SELECT 'Y', '2' FROM dual
UNION ALL SELECT 'Y', 'a' FROM dual
UNION ALL SELECT 'Z', '4' FROM dual
UNION ALL SELECT 'Z', '4' FROM dual
UNION ALL SELECT 'Z', 'b' FROM dual
UNION ALL SELECT 'Q', '1' FROM dual
UNION ALL SELECT 'Q', '2' FROM dual
UNION ALL SELECT 'Q', '3' FROM dual
UNION ALL SELECT 'Q', 'a' FROM dual
UNION ALL SELECT 'W', '4' FROM dual
UNION ALL SELECT 'W', '5' FROM dual
UNION ALL SELECT 'W', 'b' FROM dual
),
data_agg AS (
SELECT cola, listagg(colb) WITHIN GROUP (ORDER BY colb) AS agg_colb
FROM data
GROUP BY cola
)
SELECT cola
FROM data_agg da
WHERE EXISTS (SELECT 1
FROM data_agg
WHERE cola != da.cola
AND agg_colb = da.agg_colb
)
;
Edit: ops, for some reason I thought you were using Oracle... Hopefully, you'll be able to modify above query to be able to use it.