Conditional Summing in SQL with special output - sql

I need to create a SQL select statement but got some problems with it. Maybe someone can help out. My table looks something like this:
Article
Name
Amount
Location
Count
A0
Name0
10
99
1
A0
Name0
50
44
1
A0
Name0
20
44
1
A1
Name1
300
44
1
A1
Name1
250
110
1
A2
Name2
10
99
0
A3
Name3
20
1000
1
A4
Name4
NULL
NULL
1
I need to do a select statement that will sum up the amount per article in certain locations, if count = 1. My idea was to do something like this:
SELECT article, name, sum(amount), location
FROM test
WHERE count = 1
AND (location IN (44, 99)
OR location IS NULL)
GROUP BY article, name, location;
This will result in something like this:
ARTICLE
NAME
SUM(AMOUNT)
LOCATION
A0
Name0
70
44
A0
Name0
10
99
A4
Name4
-
-
A1
Name1
300
44
The only problem is, I would also need article A3 with an amount and location of 0 or NULL in the output table. So if there are no articles found in the location 44 or 99, the row must not be ommited and contains amount 0 and location 0. But if there are products in locations 99 or 44, there must not be a extra line with amount 0 and location 0.
Is this even possible with SQL? Thank you so much for your answers!

How about UNION of two SELECT statements?
1st returns "normal" data (rows that satisfy conditions you mentioned; count = 1 and location is 44 or 99)
2nd returns rows whose count is 1, but location doesn't match (NOT EXISTS clause)
Something like this:
SQL> select t.article, t.name, sum(t.amount) amount, t.location
2 from test t
3 where t.count = 1
4 and t.location in (44, 99)
5 group by t.article, t.name, t.location
6 union all
7 select t.article, t.name, sum(t.amount) amount, null
8 from test t
9 where t.count = 1
10 and not exists (select null from test a
11 where a.name = t.name
12 and a.location in (44, 99)
13 )
14 group by t.article, t.name, t.location
15 order by article, name, location;
ARTICLE NAME AMOUNT LOCATION
---------- ----- ---------- ----------
A0 Name0 70 44
A0 Name0 10 99
A1 Name1 300 44
A3 Name3 20
A4 Name4
SQL>

You could use conditional aggregation:
SELECT article, name,
sum(case when count = 1 and (location in (44, 99) or location is null) then amount end),
location
FROM test
GROUP BY article, name, location;

Related

Linking different ids through interactor

I want to link different ids through the common interactor. It is bit complex but i will try my best to frame the problem.
Here are the list of steps
1. Extract id from table A.
Table A
ID Interactor
1 30
2 40
Get the list of interactors corresponding to id from table B. for example,
select * from table B where id = 1
Table B
ID Interactors
1 30
1 32
1 33
1 36
1 38
1 39
Iterate through each interactor from the list and get the list of ids from table A.
Table A
ID Interactors
1 30
70 32
76 33
Null 36
89 38
75 39
2 45
2 40
2 43
4.Join these different ids so that when i select 1 i should get the below result.
Select * where id = 1
Result
ID Interactors
1 30
70 32
76 33
89 38
75 39
I want to achieve this using sql.
Try this:
select B.ID, B.Interactors
from A inner join B
where A.Interactors = B.Interactors
and A.ID = 1
From step 3 you have table A, and before that you have table B.
You can use simple inner join with some where condition to get your desired result.
Select Id, Interactors from
( select tableA.id, tableA.Interactors
from tableA
inner join tableB
on tableA.Interactors = tableB.Interactors
and tableA.Id is not null --- this is required since in your output record having NULL id correspond to tableA is not considered
) as db
where db.Id = 1 ---- you can apply any filter over there to get your desired result.

how to find the total

I have a table A and the output expected is below.
Table A
Id patientId PID
1 123 p1
1 123 p2
1 124 p3
1 124 p4
1 125 p5
2 126 p6
2 126 p7
2 126 p8
2 127 p9
2 127 p10
Count of pid is the count for every patientId how many pids are present and Total count of IDs is the total number of Ids(lets say 5 for Id 1 for example)
Expecting an output like this:
id patientId Count of pid Total count of IDs
1 123 2 5
1 124 2 5
1 125 1 5
2 126 3 5
2 127 2 5
I am not sure how to go beyond this
select Id,patientId,count(PID)
from A
group by 1,2
Because you want to count over two different fields, you need two separate GROUP BY subqueries, which can be JOINed on Id:
SELECT A1."Id",
A1."patientId",
A1.num_pids,
A2.total_ids
FROM (SELECT "Id", "patientId", COUNT(*) AS num_pids
FROM A
GROUP BY "Id", "patientId") A1
JOIN (SELECT "Id", COUNT(*) AS total_ids
FROM A
GROUP BY "Id") A2 ON A2."Id" = A1."Id"
ORDER BY "Id", "patientId"
Output:
Id patientId num_pids total_ids
1 123 2 5
1 124 2 5
1 125 1 5
2 126 3 5
2 127 2 5
select a.Id,a.patientId,count(a.patientId), a2.IdCount
from A a
left join (select Id, count(Id) as "IdCount"
from A
group by Id) a2
on a.Id = a2.Id
group by a.Id,a.patientId, a2.IdCount
I think you just want a window function:
select Id, patientId, count(*),
count(*) over ()
from A
group by 1, 2;
The second count(*) counts the number of rows in the result set, which appears to be what you want.

Incrementally comparing multiple value sets or lists between two tables in Oracle

I am trying to compare two sets of values between 2 Oracle tables as below. I am trying to look for and match groups of data in table B with those in table A. The group number is common between tables
Its considered a match only if all groups and values under an id in Table A are equal the group and value pair in Table B. I have highlighted the 'matches' in green. Table A could have variable number of group/value pairs under ida value. There could be ids that have only one group/value pair and there could be some that have 3 group/value pairs
Comparison Example
Ida GroupA Vala|GroupB Valb| Match?
------------------------------------------------------------------------
50 1 4 | 1 1 | No - Value doesn't match
56 1 5 | 1 1 | No - Value doesn't match
57 1 1 | 1 1 | Yes - Both Groups (1&2) and Values match
57 2 101 | 2 101 | Yes - Both Group (1&2)and Values match
94 1 1 | 1 1 | Yes - Group and Value match
96 1 1 | 1 1 | No - Only group 1 matches
96 2 102 | 2 101 | No - Only group 1 matches. Group 2 doesn't
Trial (and Error!)
I figured I would have to use some sort of count and tried using a partition by to count the groups in Table A. But, I am not sure how to use this in a query to do a sequential/multi value comparison. I looked up hierarchical functions but realized they may not fit here.. What would be the best approach to deal with such data comparison? Thanks for your help..
Happy Halloween! :)
select a.*,MAX(a.groupa) OVER (PARTITION BY a.ida ORDER BY a.groupa desc)
occurs
from tab_a a, tab_b b
where a.groupa=b.groupb and a.vala=b.valb
and a.groupa<=3
Tables
Tables A and B
create table tab_a
(
ida number,
groupa number,
vala number
)
create table tab_b
(
idb number,
groupb number,
valb number
)
Data
insert into tab_a values (50,1,4);
insert into tab_a values (56,1,5);
insert into tab_a values (57,1,1);
insert into tab_a values (57,2,101);
insert into tab_a values (58,1,1);
insert into tab_a values (58,2,104);
insert into tab_a values (60,2,102);
insert into tab_a values (94,1,1);
insert into tab_a values (95,1,1);
insert into tab_a values (95,2,101);
insert into tab_a values (96,1,1);
insert into tab_a values (96,2,102);
insert into tab_a values (97,1,1);
insert into tab_a values (97,2,101);
insert into tab_a values (97,3,201);
insert into tab_b values (752,1,1);
insert into tab_b values (752,2,101);
insert into tab_b values (752,3,201);
I don't think this is all the way there but might get you started. You can do:
select a.*, b.*,
count(case when a.groupa = b.groupb and a.vala = b.valb then a.ida end)
over (partition by a.ida) match_count,
count(distinct a.groupa||':'||a.vala)
over (partition by a.ida) val_count
from tab_a a
full outer join tab_b b on b.groupb = a.groupa and b.valb = a.vala
where a.groupa <= 3;
The distinct may not be needed, and the concatenation with the colon needs to use a characters that isn't in any real value, I suppose, to avoid potential for false matched.
That gets:
IDA GROUPA VALA IDB GROUPB VALB MATCH_COUNT VAL_COUNT
--- ------ ---- ---- ------ ---- ----------- ----------
50 1 4 0 1
56 1 5 0 1
57 1 1 752 1 1 2 2
57 2 101 752 2 101 2 2
58 1 1 752 1 1 1 2
58 2 104 1 2
60 2 102 0 1
94 1 1 752 1 1 1 1
95 1 1 752 1 1 2 2
95 2 101 752 2 101 2 2
96 1 1 752 1 1 1 2
96 2 102 1 2
97 1 1 752 1 1 3 3
97 2 101 752 2 101 3 3
97 3 201 752 3 201 3 3
And then use that as a CTE or inline view and decode the results:
with t as (
select a.ida, a.groupa, a.vala, b.groupb, b.valb,
count(case when a.groupa = b.groupb and a.vala = b.valb then a.ida end)
over (partition by a.ida) match_count,
count(distinct a.groupa||':'||a.vala)
over (partition by a.ida) val_count
from tab_a a
full outer join tab_b b on b.groupb = a.groupa and b.valb = a.vala
where a.groupa <= 3
)
select ida, groupa, vala, groupb, valb,
case
when match_count = 0 then 'No - Value doesn''t match'
when match_count = val_count and val_count = 1
then 'Yes - Group and Value match'
when match_count = val_count and val_count = 2
then 'Yes - Both Group (1&2) and Values match'
when match_count < val_count and val_count = 2 and valb is not null
then 'No - Only group 1 matches'
when match_count < val_count and val_count = 2 and valb is null
then 'No - Only group 1 matches. Group 2 doesn''t'
else 'Unknown scenario?'
end as "Match?"
from t;
Which gets:
IDA GROUPA VALA GROUPB VALB Match?
--- ------ ---- ------ ---- ------------------------------------------
50 1 4 No - Value doesn't match
56 1 5 No - Value doesn't match
57 1 1 1 1 Yes - Both Group (1&2) and Values match
57 2 101 2 101 Yes - Both Group (1&2) and Values match
58 1 1 1 1 No - Only group 1 matches
58 2 104 No - Only group 1 matches. Group 2 doesn't
60 2 102 No - Value doesn't match
94 1 1 1 1 Yes - Group and Value match
95 1 1 1 1 Yes - Both Group (1&2) and Values match
95 2 101 2 101 Yes - Both Group (1&2) and Values match
96 1 1 1 1 No - Only group 1 matches
96 2 102 No - Only group 1 matches. Group 2 doesn't
97 1 1 1 1 Yes - All Group (1&2&3) and Values match
97 2 101 2 101 Yes - All Group (1&2&3) and Values match
97 3 201 3 201 Yes - All Group (1&2&3) and Values match
I think that gets the match result you showed in your examples; not sure if the others you didn't show are what you want... ID 97 matches on three groups/values, and it's easy enough to do:
when match_count = val_count and val_count = 3
then 'Yes - All Group (1&2&3) and Values match'
for that exact match, but figuring out what to show if one or two of those three match is trickier. You could also capture the min and max B values that do match and work out from those which one(s) are missing; but then you might add a fourth group, and it doesn't scale.
This query should work:
select a.ida
from tab_a a
where a.groupa||a.vala in
(select b.groupb|| b.valb from tab_b b where b.groupb = a.groupa )
group by a.ida
having count(distinct a.groupa||a.vala) =
(select count(distinct a1.groupa||a1.vala)
from tab_a a1
where a1.ida = a.ida)
Bit of explanation:
1. where clause gets all the rows from tab_a
that exist in tab_b for a group+val combo.
- So let's say there are 2 (out of 2) rows in tab_a
that match with 2(out of 3) rows in tab_b.
2. left hand side of the having clause adds
a condition to the found rows such that
total number of rows of distinct group+val must equal to
- So here we start comparing that count 2
3. right hand side of the having clause
that provides the total number of
distinct group+val (regardless of any match with tab_b).
- here we enforce that left hand side must be equal
to the total number of rows found. So if in #2 above,
only 1 row of table_a matched (out of its 2 rows),
then #3 will exclude that set.
It's not the perfect one but match_strength 2 means that both are matched and match_strength 1 means you match only one column.
select * from (
select a.*, b.*, case when (a.vala = b.valb and a.groupa = b.groupb) then 2
when (a.vala = b.valb or a.groupa = b.groupb) then 1
else 0 end as match_strength,
row_number() over (partition by a.rowid order by
case when (a.vala = b.valb and a.groupa = b.groupb) then 2
when (a.vala = b.valb or a.groupa = b.groupb) then 1
else 0 end desc) r
from tab_a a, tab_b b)
where r = 1;
If you want to know exactly which column matches you can play with order by clause.
Assuming the requirement is to find all the ida for which all the pairs groupa, vala can be found in table_b (with no further information on why the ones that failed, failed) you could use the query below. The inner query actually shows why the ones that failed, failed (if you select * instead of just the ida). There is only one unusual thing in this solution - I have heard of the use of IN condition (and similar) for pairs, or tuples in general, instead of scalar values, but I hadn't used it till today. I just tested on your data and it works perfectly fine.
This works in the following general sense: it is not necessary to assume that groupa is unique for each ida, or the same for table_b; that is, (ida, groupa) does not have to be unique in the first table, nor does (idb, groupb) in the second table.
select distinct ida from tab_a where ida not in
(select ida from tab_a where (groupa, vala) not in (select groupb, valb from tab_b));
IDA
------
57
95
94
97

Re-Organize Access Table by converting Rows to Columns

I'm pretty new to access and SQL and need some help re-organizing a table. I have the following table (sorry for the table below - having trouble posting):
ID GroupID Distance Code Start_Finish
1 44 7 A S1
2 44 14 A F1
3 45 12 B S1
4 45 16 B F1
5 45 31 C S2
6 45 36 C F2
7 45 81 B S3
8 45 88 B F3
And need for the table to be transformed into:
GroupID Code Start_Distance Finish_Distance
44 A 7 14
45 B 12 16
45 C 31 36
45 B 81 88
try something like this
Select GroupID, Code, min(distance) as Start_distance, max(distance) as Finish_distance
from Table
group by GroupID, Code
If the min and max functions don't give you what you need, try it with First() and Last() instead.
Oops - just noticed you have 2 different entries in the output for GroupID 45 Code B - is that a requirement? With that data structure and requirement, the problem gets much more difficult.
Now I see the final column in the 1st table - I think that can be used to get the output you want:
Select GroupID, Code, mid(start_finish,2) as T, min(distance) as Start_distance, max(distance) as Finish_distance
from Table
group by GroupID, Code, T
You can use conditional aggregation for this.
select GroupID
, CODE
, max(case when Left(Start_Finish, 1) = 'S' then Distance end) as Start_Distance
, max(case when Left(Start_Finish, 1) = 'F' then Distance end) as Finish_Distance
from SomeTable
group by GroupID
, CODE

Database Query Help required in MySQL

I am looking for help in writing a query of retrieving the values from 2 tables in MySQL.
The scenario is
Table A
ID Name Marks
===================
23 John 67
45 Mark 45
12 Ram 87
Table B has the following Structure
ID Name Evaluation Marks
==============================
45 Mark 34
78 Chris 09
98 Nancy 10
23 John 12
I am trying to write a query, where if I execute the following query
Select "SOMETHING" from Table A where Id=45
I should get Marks Column as 45+34=79, which should fetch and sum from the both the Tables A and Table B.
If I execute the query with the Id=12.
Since the Id=12, does not exists in the Table B, I should get the Marks as 87.
What would a query for the above?
I assume that the id occurs only once in your tables table a, but could be missing in both. If it always exists in table a, you can use a LEFT JOIN instead of the UNION.
SELECT COALESCE(SUM(marks), 0)
FROM
(
SELECT marks FROM a WHERE id = 45
UNION ALL
SELECT SUM(evaluation_marks) AS marks FROM b WHERE id = 45
) x
Edit
If you have all users in table a, then use
SELECT a.marks + COALESCE( SUM( b.evaluation_marks ), 0 )
FROM a
LEFT OUTER JOIN b ON ( b.id = a.id )
WHERE a.id = 45
GROUP BY a.id, a.marks
You should consider changing your table model though. Why do you store name and id twice? Can't you do it like that:
id name marks evaluation marks
=======================================
12 Ram 87 0
23 John 67 12
45 Mark 45 34
78 Chris 0 9
98 Nancy 0 10