Query tables and columns based on table data - sql

Without knowing the name of a table and columns, I want to query the database retrieve the table and column names and then query the given tables.
I have an Oracle database schema that is like the following:
Item table:
Item_id, Item_type,
=================
1 box
2 book
3 box
Book table:
Item_id, title, author
===========================
2 'C# Programer', 'Joe'
Box table:
Item_id, Size
=====================
1, 'Large'
3, 'X Large'
Column_mapping table
Item_type, column_name, display_order
=====================================
box, Size, 1
book, title, 1
book, author 2
Table_mapping table:
Item_type, Table_name
========================
box, Box
book, Book
I would like a SQL statement that would give something like the following results:
Item_id, Item_type column1 column2
====================================
1, box, 'Large', <null>
2, book, 'C# Programer', 'Joe'
3, box, 'X Large', <null>
When I tried the simplified query
select *
from
(select Table_name
from Table_mapping
where Item_type = 'box')
where
Item_id = 1;
I get an error that Item_id is invalid identifier
and if I try
select *
from
(select Table_name
from Table_mapping
where Item_type = 'box');
I just get
Table_name
===========
Box
I am not sure how to proceed.

One way is to join both table and then use a coalesce on the column that can contain data from either table
SELECT
i.Item_id,
i.Item_type,
COALESCE(b.title, bx.size) column1,
b.author column2
FROM
Item i
LEFT JOIN Book b
ON i.item_id = b.item_id
LEFT JOIN Box bx
ON i.item_id = bx.item_id
Depending on how large your datasets are you may want to add a filter on the join e.g.
LEFT JOIN Book b
ON i.item_id = b.item_id
and i.item_type = 'book'
LEFT JOIN Box bx
ON i.item_id = bx.item_id
and i.item_type = 'box'
See it work at this SQLFiddle
If you wanted to do something based on the data in table_mapping or column_mapping you'd need to use dynamic SQL

Basically it is two separate queries. One for boxes and one for books. You can use union to merge the result sets together.
select i.Item_id, i.Item_type, b.size, null
from Item i inner join Box b on i.Item_id=b.Item_id
where i.Item_type = "box"
UNION
select i.Item_id, i.Item_type, b.title, b.author
from Item i inner join Book b on i.Item_id=b.Item_id
where i.Item_type = "book"

ORACLE actually stores the table- and column names in its data dictionary, so there is no need for you to maintain those data separately. Try this to get the table names:
SELECT table_name FROM user_tables;
Then do this to get the columns for each table:
SELECT column_name FROM user_tab_columns WHERE table_name = 'MYTABLE';
Once you do that, you will need to create a stored procedure in order to execute dynamic SQL. I don't think you can do this in a plain-vanilla query.

Related

Which query is more performant using groupBy in subquery?

I have next task:
I have table columns and I have table tasks - One Column has Many Tasks, Task belongsTo one Column.
First query^
SELECT id,
name,
color,
created_at,
CASE
WHEN jt.tc IS NULL THEN 0
ELSE jt.tc
END
FROM columns AS c1
LEFT JOIN
(SELECT count(*) AS tc,
column_id
FROM tasks AS t
GROUP BY column_id) AS jt ON c1.id=jt.column_id
WHERE board_id = 'some id here';
In that case in jt table will be grouped all records from tasks table and with big amount of data in tasks it will not work at all (very slow slow speed)
Second query:
SELECT id,
name,
color,
created_at,
CASE
WHEN jt.tc IS NULL THEN 0
ELSE jt.tc
END
FROM columns AS c1
LEFT JOIN
(SELECT count(*) AS tc,
column_id
FROM tasks AS t
LEFT JOIN columns c ON t.column_id = c.id
WHERE c.board_id = 'some id here'
GROUP BY column_id) AS jt ON c1.id=jt.column_id
WHERE board_id = 'some id here';
In that case within jt table will be only those columns which i need, so where clause will cut selection a lot.
Am i right?

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 Query – records within the SQL Select statement, but NOT in the table being queried

I have a large list of CustIDs that I need to query on to find if they are within the CUSTOMER table; I want the result to tell me which CustIDs ARE on the table and which CustIDs are NOT on the table.
I provided a short list below to give an idea of what I need to do.
Oracle database
Table: Customer
Primary Key: CustID
Scenario:
Customer table only has the following (2) CustID: ‘12345’, ‘56789’
Sql:
Select * from CUSTOMERS where CUSTID in (‘12345’, ‘56789’, ‘01234’);
I want the result to tell me that both ‘12345’ and ‘56789’ are in the table, AND that ‘01234’ is NOT.
select
v.CustID,
exists (select * from Customer where Customer.CustID = v.CustID)
from (values (12345), (56789), (01234)) v (CustID);
Results:
custid exists
12345 true
56789 true
1234 false
You need a left join or subquery for this. The precise syntax varies by database. Typical syntax is:
select i.custid,
(case when c.custid is not null then 1 else 0 end) as exists_flag
from (select '12345' as custid union all
select '56789' union all
select '01234'
) ci left join
customers c
on c.cust = i.custid;

Joining tables based on value

I'm having some hard time doing the join function on those two tables. I have simplified the example dataset as there are additional where-clauses involved for the first table however that doesn't seem to be a problem.
I would write the query for joining the two tables below:
select a.prod_code, a.prod_name, b.ref_value from Product_code a
left join Product_reference b on a.prod_code = b.pref_code
where a.prod_code <> 'CURTAIN' and b.ref_value = 'MAN'
The problem I'm facing is that I want to join tables kind of conditionally. I.e. if the ref_type value is 'MAN' in Product_reference table, I do want to join it, otherwise not.
For an example this query would not include "Chair" in the result as it does not have an ref_type 'MAN' available in the "Product_name". What I'd need though is still show it in the query result, just without joined value from the Product_reference table (given that value with ref_type 'MAN' does not exist for it), not leave it out altogether.
Meanwhile Product_name table record 'CURTAIN' should be left off (regardless if Product_reference ref_type 'MAN' exists or not)
Any recommendations?
Product_code
prod_code prod_name
A Table
B Chair
C Window
D Door
E Curtain
Product_reference
pref_code ref_type ref_value
A MAN x
A AUTO y
B AUTO z
C AUTO z1
C MAN x1
D AUTO zxc
E AUTO abc
E MAN cba
Move b.ref_value = 'MAN' to the join predicate:
SELECT a.prod_code, a.prod_name, b.ref_value
FROM Product_code a
LEFT JOIN Product_reference b ON a.prod_code = b.pref_code AND b.ref_value = 'MAN'
WHERE a.prod_code <> 'CURTAIN'
This will accomplish what you want, which is only left joining the data from table b where b.ref_value = 'MAN', instead of removing all other rows from the result set altogether.
Side note, thanks for including your query and sample data in your very well made question. We appreciate it.
you could use a inner join on the distinct product that have 'MAN'
select
a.prod_code
, a.prod_name
, b.ref_value
from Product_code a
inner join (
select distinct pref_code
from Product_reference
where ref_type = 'MAN') t2 on t2.pref_code = a.prod_code
and a.prod_code <> 'CURTAIN'