Concetating results from Oracle table with several criterias - sql

This is a tough one. I've read about concatating values from multible rows in a table, but can't find anything on how to go about the task set before me.
I'm not an oracle-man, and untill now have only made simple select queries, so I'm at a loss here.
In a huge oracle database table (severel hundred millions of rows) containing laboratory results, I need to select information on specific requisitions, that meet a specific criteria.
Criteria: For the same ReqNo, Analysis A B and C must be present with an answer, if they are, any instance of the answer to analysis X, Y or Z should be selected
Table contents:
ReqNo Ana Answer
1 A 7
1 B 14
1 C 18
1 X 250
2 A 8
2 X 35
2 Y 125
3 A 8
3 B 16
3 C 20
3 Z 100
4 X 115
4 Y 355
5 A 6
5 B 15
5 C 22
5 X 300
5 Y 108
5 C 88
Desired result:
ReqNo A B C X Y Z
1 7 14 18 250
3 8 16 20 100
5 6 15 22 300 108 88
leaving out ReqNo 2 and 4, since they don't meet the A/B/C criteria.
Is that even possible?

You may first filter the records that have all 3 (A,B and C) and then use PIVOT to convert them to columns for those which satisfy the criteria.
with req
AS
(
select reqno from t where ana IN ('A','B','C')
GROUP BY reqno HAVING
count(DISTINCT ana) = 3
)
select * FROM
(
select * from t where
exists ( select 1 from req r where t.reqno = r.reqno )
)
PIVOT(
min(answer) for ana in ('A' as A, 'B' as B, 'C' as C,
'X' as X, 'Y' as Y, 'Z' as Z)
) ORDER BY reqno;
Demo

I would just use conditional aggregation:
select reqno,
max(case when Ana = 'A' then Answer end) as a,
max(case when Ana = 'B' then Answer end) as b,
max(case when Ana = 'C' then Answer end) as c,
max(case when Ana = 'X' then Answer end) as x,
max(case when Ana = 'Y' then Answer end) as y,
max(case when Ana = 'Z' then Answer end) as z
from t
group by reqno
having sum(case when Ana = 'A' then 1 else 0 end) > 0 and
sum(case when Ana = 'B' then 1 else 0 end) > 0 and
sum(case when Ana = 'C' then 1 else 0 end) > 0 ;
Given that you don't seem to have duplicates, you can simplify the having to:
having sum(case when Ana in ('A', 'B', 'C') then 1 else 0 end) = 3

Related

Group by and divide

I have the table below on sql of accounting accounts (A is a cost account and B a headcount ) , and cc as cost center . So what i want to do is divide the amount of account A over account B for each cc
Account
cc
Amount
A
x
1
A
y
2
B
z
4
B
y
1
A
z
1
B
x
2
So the result would be :
Account
cc
Amount
A
x
1 /2
A
y
2
B
z
0
B
y
0
A
z
1 /4
B
x
0
I was thinking about a group by but Im a very beginner in sql and don't know how to use it thanks in advance for your help!
For the results you specify, you can use window functions:
select t.*,
(case when account = 'A'
then amount * 1.0 / sum(case when account = 'B' then amount end) over (partition by cc)
else 0
end) as ratio
from t;
I would just use a regular group by with some cases:
with cc_amts as (
select cc,
sum(case when account = 'A' then amount else 0 end) amt_a,
sum(case when account = 'B' then amount else 0 end) amt_b
from t
group by cc)
select cc, amt_a / amt_b
from cc_amts;

Convert Rows into column without using Pivot

I have a data set, but the sample data will look like the below one:
Country Date Category X Y
IN 2011-11-22 B 1 0
BA 2010-11-23 B 11 0.2
IN 2011-11-22 A 1 0
BA 2011-11-23 A 1 1
IN 2011-07-28 A 1 0
want to convert it into : Output
Country Date B_X B_Y A_X A_Y
IN 2011-11-22 1 0 1 0
BA 2010-11-23 11 0.2 1 1
IN 2011-07-28 0 0 1 0
I have tried using case but it is not giving me a desired output, can anyone help!!
I think you just want to aggregate by country/date and use conditional aggregation:
select country, date,
sum(case when category = 'B' then x end) as x_b,
sum(case when category = 'B' then y end) as y_b,
sum(case when category = 'A' then x end) as x_a,
sum(case when category = 'A' then y end) as y_a
from t
group by country, date;

Teradata Scenario

Given:-
When DML_OPeration is Insert "I" then B=0 When DML_OPeration is
Delete "D" then B will hold the value of A of inserted record
Condition :-
if COUNT OF 'I' = Count of 'D', then we don't need those records. for example : ID=111
Find latest insert('I') DML_operation
ID A B DML_Operation
1 111 1 0 I
2 111 2 1 D
3 111 3 0 I
4 111 4 3 D
5 111 5 0 I
6 111 6 5 D
7 111 7 0 I
8 222 8 0 I
9 333 9 0 I
10 333 10 9 D
11 444 11 0 I
12 444 12 11 D
13 444 13 0 I
14 111 14 7 D
15 333 15 0 I
16 444 16 0 I
17 444 17 13 D
Desire Output
ID A B DML_Operation
-------------
222 8 0 I
333 15 0 I
444 16 0 I
My logic which is not working
sel ID, Max(A) from xyz
group by ID
having count(c='I') <> COUNT(c='D')
does you find like below
select ID, Max(A) from xyz
group by ID
having sum(case when c='I' then 1 else 0 end) <> sum(case when c='D' then 1 else 0 end)
How about using case?
select ID, Max(A)
from xyz
group by ID
having sum( case when c = 'I' then 1 else 0 end) <> sum(case when c = 'D' then 1 else 0 end)
Or:
having sum(case when c = 'I' then 1
when c = 'D' then -1
else 0
end) <> 0
This will find all 'I' rows without matching 'D' row:
SELECT *
FROM mytab AS t1
WHERE DML_Operation = 'I'
AND NOT EXISTS
( SELECT *
FROM mytab AS t2
WHERE t2.id = t1.id
AND t2.b = t1.a
AND DML_Operation = 'D'
)

sql query different column based on input

I am using MS-SQL 2008. I have a table with different columns based on locations in it that will have a 'Y' or Null value. The table also has other data other than location from survey results. I have set up a temptable #TempLocation to hold the location based on the one or all. I need to select rows from the table based on 'Y' from one or more location rows within a date range.
TableID Northwest Northeast Southwest Southeast Batchno first_choice date_completed
1 Y Y Y 1 A 2012-11-10
2 Y Y 1 SA 2012-19-10
3 Y Y 1 N 2012-07-10
4 Y Y Y 2 A 2012-10-10
5 Y 2 A 2012-16-10
6 Y Y 2 D 2012-21-10
7 Y NULL A 2012-19-10
8 Y Y Y Y 3 SA 2012-11-10
9 Y 3 A 2012-10-10
10 Y Y 3 A 2012-07-10
I have created a Dynamic SQL statement to pull one location successfully but is it possible to pull all of them?
select ''' + (SELECT * FROM #TempLocation) + ''',
count(batchno),
count(case when first_choice is not null then batchno end),
count(case when t.First_choice =''SD'' then 1 end) ,
count(case when t.First_choice=''D'' then 1 end) ,
count(case when t.First_choice=''N'' then 1 end) ,
count(case when t.First_choice=''A'' then 1 end) ,
count(case when t.First_choice=''SA'' then 1 end)
from customer_satisfaction_survey t
where t.date_completed>= ''' + CAST(#beg_date AS VARCHAR) + '''
and t.date_completed < ''' + CAST(dateadd(day,1,#end_date) AS Varchar) + '''
and t.' + (SELECT * FROM #TempLocation) + ' = ''Y'''
An All result would look like this.
Number Location Total Total2 SA A N D SD
1 Northwest 6 6 1 3 1 1 0
2 Northeast 5 4 2 2 1 0 0
3 Southwest 4 4 1 3 0 0 0
4 Southeast 6 6 2 3 0 1 0
I have to think that you are approaching this in the wrong way, because your data is not normalized. The first thing you should do is to normalize the data using UNPIVOT. I'm assuming that you are using SQL Server, since your syntax suggests that. It is a good idea to tag all questions with the database, though.
You can unpivot your data with a statement such as:
select BatchNo, FirstChoice, DateCompleted, Location
from d
unpivot (val for location in (Northwest, Northeast, Southwest, Southeast)) as unpvt
Next, set up your temporary table to have a separate row for each location. Then, you can do the join with no dynamic SQL. Something like:
with dnorm as (
THE NORMALIZATION QUERY HERE
)
select dnorm.location, count(*) as total,
sum(case when dnorm.first_choice is not null then 1 else 0 end) as total2,
sum(case when dnorm.first_choice = 'SA' then 1 else 0 end) as SA,
. . .
from dnorm join
#TempLocation tl
on dnorm.location = tl.location
where ALL YOUR WHERE CONDITIONS HERE
The final query looks something like:
with dnorm as (
select BatchNo, FirstChoice, DateCompleted, Location
from d
unpivot (val for location in (Northwest, Northeast, Southwest, Southeast)) as unpvt
)
select dnorm.location, count(*) as total,
sum(case when dnorm.first_choice is not null then 1 else 0 end) as total2,
sum(case when dnorm.first_choice = 'SA' then 1 else 0 end) as SA,
. . .
from dnorm join
#TempLocation tl
on dnorm.location = tl.location
where ALL YOUR WHERE CONDITIONS HERE
The dynamic SQL approach is quite clever, but I don't think it is the simplest way to approach this.

T-SQL Query a matrix table used as LIFO

Following my [question]: T-SQL Query a matrix table for free position
I've now trying to handle my matrix table as a LIFO. Each couple of (X,Z) represent a channel in which I can store an element. When I generate a location I'm now using the query provided in the above question and here below.
SELECT x, z, MAX(CASE WHEN disabled = 0 AND occupiedId IS NULL THEN Y ELSE 0 END) firstFreeY
FROM matrix
GROUP BY x, z
ORDER BY x, z;
This is working but it doesn't handle "holes". In fact It's possible that a Disabled flag is removed from the table or an element is manually deleted.
In case my Matrix table will look like this:
X Z Y Disabled OccupiedId
--------------------------------------------------
1 1 1 0 591
1 1 2 0 NULL
1 1 3 1 NULL
1 1 4 0 524
1 1 5 0 523
1 1 6 0 522
1 1 7 0 484
1 2 1 0 NULL
1 2 2 0 NULL
1 2 3 0 NULL
1 2 4 0 NULL
1 2 5 0 NULL
1 2 6 0 589
1 2 7 0 592
the result of the above query is:
X Z firstFreeY
------------------------
1 1 2
1 2 5
instead of:
X Y firstFreeY
------------------------
1 1 0
1 2 5
Any suggestions on how to achieve this?
This query looks for the largest Y that is smaller than all other occupied Y's:
select m1.X
, m1.Z
, max(
case
when m2.MinOccupiedY is null or m1.Y < m2.MinOccupiedY then m1.Y
else 0
end
) as FirstFreeY
from matrix m1
join (
select X
, Z
, min(
case
when disabled <> 0 or occupiedId is not null then Y
end
) as MinOccupiedY
from matrix
group by
X
, Z
) m2
on m1.X = m2.X
and m1.Z = m2.Z
group by
m1.X
, m1.Z
Live example at SQL Fiddle.
just to know if i understood what you were asking, is this working too?
select distinct
m1.x,m1.z, o.y
from
matrix m1
cross apply
(
select top 1 (case when m2.Disabled = 0 then m2.y else 0 end)
from matrix m2
where
m1.x = m2.x
and m1.z = m2.z
and m2.OccupiedId is null
order by m2.y desc
) o (y);