Sum and Join Two tables in Oracle - sql

I have two tables: Base table and Detail table. I need to get the count from the Detail table with Base table.
Tables structure
Base table
Nid UserId BaseVal
--------------------
1 150 100
Detail table
Did Nid SeqVal
----------------
1 1 500
2 1 600
I want this:
Result Table
BaseVal SeqVal
---------------
100 1100
I have tried this query but I can get only the Summation value. I need BaseVal too.
SELECT SUM(SeqVal) as SEQVAL
FROM TBL_DETAIL
WHERE Nid =: Nid

You have to join tables first and then calculate the sum:
select b.nid, b.baseVal, sum(d.seqVal)
from base_table b join detail_table d on b.nid = d.nid
where b.nid =: Nid
group by b.nid, b.baseVal

There are multiple answers possible. Assuming your base table always has 1 record and detail table has multiple rows per base table record, a possible answer can be
SELECT
AVG(BaseVal) Sum_BaseVal, SUM(SeqVal) Sum_SeqVal
From BaseTable T1, DetailTable T2
WHERE T1.Nid = T2.Nid
AND T1.Nid = :1
Bind T1.Nid with right value to ensure you get values for required Nid.
Note: You can also use above query with a GROUP BY Nid (it may be necessary if your problem statement is simplified version of a complex query)
Other possible answers can be (I'm giving only 1 to save time, but more exists)
WITH
SeqSum as (SELECT SUM(SeqVal) Sum_SeqVal FROM DetailTable WHERE Nid = 1)
SELECT BaseVal, Sum_SeqVal
FROM BaseTable , SeqSum
WHERE Nid = 1
You can further tune queries to have proper conditions if any..

Related

How to use INTERSECT together with COUNT in SQLite?

I have a table called customer_transactions and a table called blacklist.
The customer_transactions table has a column called atm_name.
Both tables share a unique key called id.
How can I intersect the two tables in such a way that the query shows me
customers that appear on both tables.
a corresponding column that displays the times that they had used a
certain atm alongside the atm's name
(for instance: id_1 -- bank of america -- 2; id_1 -- citibank -- 3;
id_2 -- bank of america -- 1; id_2 -- citibank -- 4, etcetera).
I have something like this
SELECT id,
atm_name,
count(atm_name) as atm_count
FROM customer_transactions
GROUP BY id, atm_name
How can I INTERSECT this table with the blacklist table and maintain what I currently have as output?
Thanks in advance.
You seem to want a join. Assuming that column id relates the two tables, and that it is a unique key in blacklist, you can do:
select ct.id, ct.atm_name, count(*) as atm_count
from customer_transactions ct
inner join blacklist b on b.id = ct.id
group by ct.id, ct.atm_name
You can also express this logic with exists and a correlated subquery:
select ct.id, ct.atm_name, count(*) as atm_count
from customer_transactions ct
where exists (select 1 from blacklist b where b.id = ct.id)
group by ct.id, ct.atm_name

Join table using column value as table name

Is it possible to join a table whereby the table name is a value in a column?
Here is a TABLE called food:
id food_name price_table pricing_reference_id
1 | 'apple' | 'daily_price' | 13
2 | 'banana' | 'monthly_price' | 13
3 | 'hotdog' | 'weekly_price' | 17
4 | 'sandwich' | 'monthly_price' | 9
There are three other tables (pricing tables): daily_price, weekly_price, and monthly_price tables.
Side note: Despite their names, the three pricing tables display vastly different kinds of information, which is why the three tables were not merged into one table
Each row in the food table can only be joined with one of the three pricing tables at most.
The following does not work -- it is just to illustrate what I am trying to get at:
SELECT *
FROM food
LEFT JOIN food.price_table ON food.pricing_reference_id = daily_price.id
WHERE id = 1;
Obviously the query does not work. Is there any way that the name of the table in the price_table column could be used as the table name in a join?
I would suggest left joins:
select f.*,
coalesce(dp.price, wp.price, mp.price) as price
from food f left join
daily_price dp
on f.pricing_reference_id = dp.id and
f.pricing_table = 'daily_price' left join
weekly_price wp
on f.pricing_reference_id = wp.id and
f.pricing_table = 'weekly_price' left join
monthly_price mp
on f.pricing_reference_id = mp.id and
f.pricing_table = 'monthly_price' ;
For the columns you reference, you need to use coalesce() to combine the results from the three tables. You say that the tables have different data, so you would need to list the columns separately.
The main reason I recommend this approach is performance. I think the left joins should be faster than any solution that uses union all.
Could you get your expected result using by a derived table with UNION SELECT which has a column of each table name?
SELECT *
FROM food
LEFT JOIN
(
SELECT 'daily_price' AS price_table, * FROM daily_price
UNION ALL SELECT 'monthly_price', * FROM monthly_price
UNION ALL SELECT 'weekly_price', * FROM weekly_price
) t
ON food.price_table = t.price_table AND
food.pricing_reference_id = t.id
ORDER BY food.id;
dbfiddle

SQL get latest row in one to many relationship using where clause only

I have two tables in which (roll_no) is same in both tables.
I have a table (student) having columns name and roll_no. e.g
roll_no name
----------------
123 John
It has relation with another table (student_changed). Which has columns name, roll_no and LastEditTime. e.g.
roll_no name LastEditTime
--------------------------------------
123 Johny 2017-11-09 06:00:00
123 John 2017-11-08 07:00:00
What I want to ask is, how can I get the join having the most recent LastEditTime.
There are many similar questions but my limitation is that I can only use/modify where clause.
e.g.
I cannot modify the select query as well.
Consider it like this
select * from student, student_changed <-- *this part cannot be changed*
where <-- This can be modified
You do not need the join. Table student_changed has all you need:
select roll_no, name, max(LastEditTime) as LastEditTime
from student_changed
group by roll_no, name
Try this query:
select roll_no,name, max(LastEditTime)
from table2
group by
roll_no,name;
Update 1:
select table1.roll_no,table1.name, max(table2.LastEditTime)
from table2 join table1 on table1.roll_no=table2.roll_no
group by
table1.roll_no,table1.name;
One table is enough for your requirement.
Update 2:
select table1.roll_no,table1.name, max(table2.LastEditTime)
from table2 join table1
where table1.roll_no=table2.roll_no
group by table1.roll_no,table1.name;
Update 3:
select table1.roll_no,table1.name,table2.LastEditTime
from table2 join table1
where
(table1.roll_no,table1.name,table2.lastEditTime) in (select table1.roll_no,table1.name, max(table2.LastEditTime) from table2 join table 1 on table1.roll_no=table2.roll_no
group by table1.roll_no,table1.name);
Thanks for all the answers but I managed to get it working by the following where clause.
where student.roll_no = student_changed.roll_no
and LASTEDITTIME in(select max(LASTEDITTIME) from student_changed where student.roll_no = student_changed.roll_no)

pad database out with NULL criteria

If I have the following sample table (order by ID)
ID Date Type
-- ---- ----
1 01/01/2000 A
2 22/04/1995 A
2 14/02/2001 B
Where you can immediate see that ID=1 does not have a Type=B, but ID=2 does. What I want to do, if fill in a line to show this:
ID Date Type
-- ---- ----
1 01/01/2000 A
1 NULL B
2 22/04/1995 A
2 14/02/2001 B
where there could potentially be 100's of different types, (so may need to end up inserting 100's rows per person if they lack 100's Types!)
Is there a general solution to do this?
Could I possibly outer join the table on itself and do it that way?
You can do this with a cross join to generate all the rows and a left join to get the actual data values:
select i.id, s.date, t.type
from (select distinct id from sample) i cross join
(select distinct type from sample) t left join
sample s
on s.id = i.id and
s.type = t.type;

Update multiple row values to same row and different columns

I was trying to update table columns from another table.
In person table, there can be multiple contact persons with same inst_id.
I have a firm table, which will have latest 2 contact details from person table.
I am expecting the firm tables as below:
If there is only one contact person, update person1 and email1. If there are 2, update both. If there is 3, discard the 3rd one.
Can someone help me on this?
This should work:
;with cte (rn, id, inst_id, person_name, email) as (
select row_number() over (partition by inst_id order by id) rn, *
from person
)
update f
set
person1 = cte1.person_name,
email1 = cte1.email,
person2 = cte2.person_name,
email2 = cte2.email
from firm f
left join cte cte1 on f.inst_id = cte1.inst_id and cte1.rn = 1
left join cte cte2 on f.inst_id = cte2.inst_id and cte2.rn = 2
The common table expression (cte) used as a source for the update numbers rows in the person table, partitioned by inst_id, and then the update joins the cte twice (for top 1 and top 2).
Sample SQL Fiddle
I think you don't have to bother yourself with this update, if you rethink your database structure. One great advantage of relational databases is, that you don't need to store the same data several times in several tables, but have one single table for one kind of data (like the person's table in your case) and then reference it (by relationships or foreign keys for example).
So what does this mean for your example? I suggest, to create a institution's table where you insert two attributes like contactperson1 and contactperson2: but dont't insert all the contact details (like email and name), just the primary key of the person and make it a foreign key.
So you got a table 'Person', that should look something like this:
ID INSTITUTION_ID NAME EMAIL
1 100 abc abc#inst.com
2 101 efg efg#xym.com
3 101 ijk ijk#fg.com
4 101 rtw rtw#rtw.com
...
And a table "Institution" like:
ID CONTACTPERSON1 CONTACTPERSON2
100 1 NULL
101 2 3
...
If you now want to change the email adress, just update the person's table. You don't need to update the firm's table.
And how do you get your desired "table" with the two contact persons' details? Just make a query:
SELECT i.id, p1.name, p1.email, p2.name, p2.email
FROM institution i LEFT OUTER JOIN person p1 ON (i.contactperson1 = p1.id)
LEFT OUTER JOIN person p2 ON (i.contactperson2 = p2.id)
If you need this query often and access it like a "table" just store it as a view.