Count occurrence of id across multiple columns - sql

Ok, what am I doing wrong here. This should be simple...
I have a table that isn't normalized. I want to get a count of the IDs that appear in three columns of the table.
1 100 200 300
2 200 700 800
3 200 300 400
4 100 200 300
result:
2 100
4 200
3 300
1 400
1 700
1 800
Here is my attempt. The the union works. It is my attempt to sum and group them that fails:
select sum(cnt), ICDCodeID from
(
select count(*) cnt, ICDCodeID1 ICDCodeID from encounter
where (ICDCodeID1 is not null) group by ICDCodeID1
UNION ALL
select count(*) cnt, ICDCodeID2 ICDCodeID from encounter
where (ICDCodeID2 is not null) group by ICDCodeID2
UNION ALL
select count(*) cnt, ICDCodeID3 ICDCodeID from encounter
where (ICDCodeID3 is not null) group by ICDCodeID3
) group by cnt, ICDCodeID
Or better way?
Here is the error I am getting: "Incorrect syntax near the keyword 'GROUP'."

Maybe this helps:
SELECT D.ICDCode,
COUNT(*) as Cnt
FROM(SELECT ICDCodeID1 AS ICDCode
FROM encounter
UNION
ALL
SELECT ICDCodeID2 AS ICDCode
FROM encounter
UNION
ALL
SELECT ICDCodeID3 AS ICDCode
FROM encounter
)D
GROUP
BY D.ICDCode;

Try this:
-- build sample data
create table temp(
id int,
col1 int,
col2 int,
col3 int
)
insert into temp
select 1, 100, 200, 300 union all
select 2, 200, 700, 800 union all
select 3, 200, 300, 400 union all
select 4, 100, 200, 300
-- start
;with cte(id, col_value) as(
select id, col1 from temp union all
select id, col2 from temp union all
select id, col3 from temp
)
select
col_value,
count(*)
from cte
group by col_value
-- end
-- clean sample data
drop table temp

Related

Oracle SQL compare aggregated lines

Kind of stuck in relatively simple SQL...
Could someone propose some code for retrieving the GroupID for aggregated lines (group by GroupID) whose aValue is different ?
For example in the below table I'd need to get GroupID '4' as the 2 Items with in the same group (4) have different aValue
GroupId ItemID aValue
4 19 Hello
4 20 Hello1
5 78 Hello5
5 86 Hello5
You can use the having clause and look at the count of distinct values:
-- CTE for your sample data
with your_table (groupid, itemid, avalue) as (
select 4, 19, 'Hello' from dual
union all select 4, 20, 'Hello1' from dual
union all select 5, 78, 'Hello5' from dual
union all select 5, 86, 'Hello5' from dual
)
select groupid
from your_table
group by groupid
having count(distinct avalue) > 1;
GROUPID
----------
4
If you actually also want to see the individual values, you can use an analytic count in a subquery and filter that with where instead of having:
-- CTE for your sample data
with your_table (groupid, itemid, avalue) as (
select 4, 19, 'Hello' from dual
union all select 4, 20, 'Hello1' from dual
union all select 5, 78, 'Hello5' from dual
union all select 5, 86, 'Hello5' from dual
)
select groupid, itemid, avalue
from (
select groupid, itemid, avalue,
count(distinct avalue) over (partition by groupid) as value_count
from your_table
)
where value_count > 1;
GROUPID ITEMID AVALUE
---------- ---------- ------
4 19 Hello
4 20 Hello1
I would do this as :
select GroupId
from table t
group by GroupId
having min(aValue) <> max(aValue);
However, if you want all columns/expression then you can use EXISTS
select t.*
from table t
where exists (select 1
from table t1
where t1.GroupId = t.GroupId and
t1.avalue <> t.avalue
);

ORACLE get rows with condition value equals something but not equals to anything else

I have rows that look like .
OrderNo OrderStatus SomeOtherColumn
A 1
A 1
A 3
B 1 X
B 1 Y
C 2
C 3
D 2
I want to return all orders that have only one possible value of orderstatus. For e.g Here order B has only order status 1 SO result should be
B 1 X
B 1 Y
Notes:
Rows can be duplicated with same order status. For e.g. B here.
I am interested in the order having a very peculiar status for e.g. 1 here and not having any other status. So if B had a status of 3 at any point of time it is disqualified.
You can use not exists:
select t.*
from t
where not exists (select 1
from t t2
where t.orderno = t2.orderno and t.OrderStatus = t2.OrderStatus
);
If you just want the orders where this is true, you can use group by and having:
select orderno
from t
group by orderno
having min(OrderStatus) = max(OrderStatus);
If you only want a status of 1 then add max(OrderStatus) = 1 to the having clause.
Here is one way to do it. It does not handle the case where the status can be NULL; if that is possible, you will need to explain how you want it handled.
SQL> create table test_data ( orderno, status, othercol ) as (
2 select 'A', 1, null from dual union all
3 select 'A', 1, null from dual union all
4 select 'A', 3, null from dual union all
5 select 'B', 1, 'X' from dual union all
6 select 'B', 1, 'Y' from dual union all
7 select 'C', 2, null from dual union all
8 select 'C', 3, null from dual union all
9 select 'D', 2, null from dual
10 );
Table created.
SQL> variable input_status number
SQL> exec :input_status := 1
PL/SQL procedure successfully completed.
SQL> column orderno format a8
SQL> column othercol format a8
SQL> select orderno, status, othercol
2 from (
3 select t.*, count(distinct status) over (partition by orderno) as cnt
4 from test_data t
5 )
6 where status = :input_status
7 and cnt = 1
8 ;
ORDERNO STATUS OTHERCOL
-------- ---------- --------
B 1 X
B 1 Y
One way to handle NULL status (if that may happen), if in that case the orderno should be rejected (not included in the output), is to define the cnt differently:
count(case when status != :input_status or status is null then 1 end)
over (partition by orderno) as cnt
and in the outer query change the WHERE clause to a single condition,
where cnt = 0
Count distinct OrderStatus partitioned by OrderNo and show only rows where number equals one:
select OrderNo, OrderStatus, SomeOtherColumn
from ( select t.*, count(distinct orderstatus) over (partition by orderno) cnt
from t )
where cnt = 1
SQLFiddle demo
Just wanted to add something to Gordon's answer, using a stats function:
select orderno
from t
group by orderno
having variance(orderstatus) = 0;

TSQL, Get top N unique rows across ordered groups

I have the following table of values, sorted by arbitrary segment id specified by the user. ( I know how to do that query and below are the results )
SegmentID SequenceID
3 100
3 200
3 400
3 430
1 100
1 200
1 300
1 410
2 100
2 200
2 300
2 420
I need a SQL query ( Sql Server 2012 ) that returns top N Records in order of Precedence where SequenceID is not repeated.
Example: user wants 7 sequences in order of segment preference: 3, 1,2.
The correct answer is
SegmentID SequenceID
3 100
3 200
3 400
3 430
1 300
1 410
2 420
in a nutshell, i need to traverse recordset from top to bottom, grab unique sequences as i go and add to the list.
How can I do that in a TSql statement?
create table #data (SegmentID int,SequenceID int);
insert into #data values
(3,100),
(3,200),
(3,400),
(3,430),
(1,100),
(1,200),
(1,300),
(1,410),
(2,100),
(2,200),
(2,300),
(2,420);
This table declares the ordering preference:
create table #prefs (Preference int, SegmentID int);
insert into #prefs values(1,3),(2,1),(3,2);
with cte as
(
select #data.SegmentID,
#data.SequenceID,
Preference,
row_number() over (partition by SequenceID order by Preference) rn
from #data
inner join #prefs on #data.SegmentID = #prefs.SegmentID
)
select SegmentId,
SequenceID
from cte
where rn = 1
order by Preference, SequenceID;
DEMO:
http://rextester.com/JKNKD15000
With cte (SegmentID, SequenceID) as
(SELECT 3, 100 UNION ALL
SELECT 3, 200 UNION ALL
SELECT 3, 400 UNION ALL
SELECT 3, 430 UNION ALL
SELECT 1, 100 UNION ALL
SELECT 1, 200 UNION ALL
SELECT 1, 300 UNION ALL
SELECT 1, 410 UNION ALL
SELECT 2, 100 UNION ALL
SELECT 2, 200 UNION ALL
SELECT 2, 300 UNION ALL
SELECT 2, 420),
userOrder (SegmentID, orderID) as (
SELECT 3, 1 UNION ALL
SELECT 1, 2 UNION ALL
SELECT 2, 3),
Results (SegmentID, SequenceID, RN, orderID) as (
Select A.*
, Row_number() over (Partition by A.SequenceID order by B.orderID) RN
, B.orderID
from cte A
INNER JOIN userOrder B
on A.SegmentID = B.SegmentID)
Select Top 7 *
from results where RN = 1
order by OrderID, SequenceID

selecting a row only if it satisfies another criteria for the similar row

Table orders
Status_id Order_number
100 ord1
200 ord2
100 ord3
300 ord2
100 Ord4
400 ord2
200 ord1
200 ord3
I need all the orders that are with the Status_id=100 or 200 BUT if the same orders have Status_id=400, it should not be selected.
In the example above, ord1,ord3, ord4 should be selected. But Ord2 should not be selected as it is also with status_id=400.
This is a type of problem that is readily solved with group by and having:
select order_number
from orders
group by order_number
having sum(case when status in (100, 200) then 1 else 0 end) > 0 and
sum(case when status = 400 then 1 else 0 end) = 0;
If you need the full details of every row, rather than just the order numbers, then you need to use analytic functions rather than aggregates; in this case, a conditional analytic count on rows where test_data=400.
with
test_data ( status_id, order_number ) as (
select 100, 'ord1' from dual union all
select 200, 'ord2' from dual union all
select 100, 'ord3' from dual union all
select 300, 'ord2' from dual union all
select 100, 'ord4' from dual union all
select 400, 'ord2' from dual union all
select 200, 'ord1' from dual union all
select 200, 'ord3' from dual
)
-- end of test data (not part of the query); SQL query begins below this line
select status_id, order_number
from ( select status_id, order_number,
count(case when status_id = 400 then 1 end)
over (partition by order_number) as st_400_cnt
from test_data
)
where status_id in (100, 200)
and st_400_cnt = 0
order by order_number, status_id -- if needed
;
STATUS_ID ORDER_NUMBER
--------- ------------
100 ord1
200 ord1
100 ord3
200 ord3
100 ord4
5 rows selected.

Oracle sql group sum

I have table With ID,Sub_ID and value coloumns
ID SUB_ID Value
100 1 100
100 2 150
101 1 100
101 2 150
101 3 200
102 1 100
SUB ID can vary from 1..maxvalue( In this example it is 3). I need Sum of values for each Sub_ID. If SUB_ID is less than MAXVALUE for a particlaur ID then it should take MAX(SUB_ID) of each ID As shown below ( In this example for ID=100 for SUB_ID 3 it should take 150 i.e 2<3 so value=150))
SUB_ID SUM(values) Remarks
1 300 (100+100+100)
2 400 (150+150+100)
3 450 (150+200+100)
This can be easily done in PL/SQL . Can we use SQL for the same using Model Clause or any other options
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TableA ( ID, SUB_ID, Value ) AS
SELECT 100, 1, 100 FROM DUAL
UNION ALL SELECT 100, 2, 150 FROM DUAL
UNION ALL SELECT 101, 1, 100 FROM DUAL
UNION ALL SELECT 101, 2, 150 FROM DUAL
UNION ALL SELECT 101, 3, 200 FROM DUAL
UNION ALL SELECT 102, 1, 100 FROM DUAL
Query 1:
WITH sub_ids AS (
SELECT LEVEL AS sub_id
FROM DUAL
CONNECT BY LEVEL <= ( SELECT MAX( SUB_ID ) FROM TableA )
),
max_values AS (
SELECT ID,
MAX( VALUE ) AS max_value
FROM TableA
GROUP BY ID
)
SELECT s.SUB_ID,
SUM( COALESCE( a.VALUE, m.max_value ) ) AS total_value
FROM sub_ids s
CROSS JOIN
max_values m
LEFT OUTER JOIN
TableA a
ON ( s.SUB_ID = a.SUB_ID AND m.ID = a.ID )
GROUP BY
s.SUB_ID
Results:
| SUB_ID | TOTAL_VALUE |
|--------|-------------|
| 1 | 300 |
| 2 | 400 |
| 3 | 450 |
Try this
SELECT SUB_ID,SUM(values),
(SELECT DISTINCT SUBSTRING(
(
SELECT '+'+ CAST(values AS VARCHAR)
FROM table_Name AS T2
WHERE T2.SUB_ID = d.SUB_ID
FOR XML PATH ('')
),2,100000)[values]) as values
FROm table_Name d
GROUP BY SUB_ID
How about something like this:
select max_vals.sub_id, sum(nvl(table_vals.value,max_vals.max_value)) as sum_values
from (
select all_subs.sub_id, t1.id, max(t1.value) as max_value
from your_table t1
cross join (select sub_id from your_table) all_subs
group by all_subs.sub_id, t1.id
) max_vals
left outer join your_table table_vals
on max_vals.id = table_vals.id
and max_vals.sub_id = table_vals.sub_id
group by max_vals.sub_id;
The inner query gets you a list of all sub_id/id combinations and their fall-back values. The out query uses an nvl to use the table value if it exists and the fall-back value if it doesn't.