Join only selected tables - sql

I have A table, B table 10 different tables(1,2,3,..10). I have to select the data by joining any one/more of those 10 , A, and B tables based on the input.
Ex: If input is only 2,5,8,10 I have to join A, B, 2,5,8, and 10 tables and retrieve the data. If input is only 1 and 7th table , I have to join A , B, 1, and 7 tables and retrieve the data.
---- sample-----
suppose A is a person table with p_id, p_name, order_id,.......
B is an order table with Order_id,.........
each 1-10 tables are shopping items like clothes, shoes, electronics,...etc
now I want to pull person details who orders only clothes and shoes with some other constraints like within these dates or age should be 20 like that
then I have to join only person table, order table, clothes table, and shoe table and retrived the details about the persons who ordered atlease one of both the tables. person having only shoe or only colthes are not required. result will be like how many clothes he orderd and how many shoes he ordered.
Can anyone please give me some idea how to do it.
I am working on oracle db and using SQL.

This is sql-server syntax as far as variables but hopefully you can translate that to oracle variables. Anyway the trick is to use your input variables as conditions on a LEFT JOIN. That way if the condition for your variable is false it just wont return rows from that table. If true it will return matches. If you really want it to be an INNER JOIN rather than a LEFT JOIN then just put a case statement in your WHERE condition to make it true if your variable condition is met.
DECLARE #IncludeT1 BIT, #IncludeT2 BIT
SELECT *
FROM
TableA a
INNER JOIN TableB B
ON a.somecol = b.somecol
LEFT JOIN Table1 t1
ON a.somecol = t1.somecol
AND #IncludeT1 = 1
LEFT JOIN Table2 t2
ON b.somecol = t2.solmcol
AND #IncludeT1 = 1
WHERE
(CASE WHEN (#IncludeT1 = 1 and t1.someid IS NOT NULL) OR #IncludeT1 = 0 THEN 1 ELSE 0 END) = 1
AND (CASE WHEN (#IncludeT2 = 1 and t2.someid IS NOT NULL) OR #IncludeT2 = 0 THEN 1 ELSE 0 END) = 1
You can also do it via dynamic SQL as the other answer suggests there are more considerations if you go that route.

I don't think you can handle this with plain SQL. It looks like you need to use dynamic SQL. Have a stored procedure with parameters for the tables you need to join and then build your query based on how you call that procedure. Run the query with execute immediate... Read more here: https://docs.oracle.com/cloud/latest/db112/LNPLS/dynamic.htm#LNPLS011
Update: Here's a sample with pseudo plsql as I don't have your actual data. This is just to give you an idea on how to proceed - I can't write you actual code with the given information and your setup:
DECLARE
table1 VARCHAR2 (100) := 'Table1';
table2 VARCHAR2 (100) := '';
table3 VARCHAR2 (100) := 'Table2';
...
table10 VARCHAR2 (100) := 'Table10';
query VARCHAR2 (4000) := '';
BEGIN
--create the core join between A and B
query := query || 'select A.p_id, A.p_name, B.order_id from A, B where A.order_id = B.order_id ';
IF (table1 != '') THEN query := query || ' and a.id = table1.id ';
--if statements for the rest of your tables and add the join condition for each table to the query string
...
EXECUTE IMMEDIATE query; --run the query... depends on what you want to do with the returned result you can use RETURN INTO or BULK COLLECT INTO...
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END;
Hope this helps...

It appears your 10 different tables are actually just 10 different categories of Products.
Is it possible to homogenise them all into one big Products table with an additional field for ProductCategory (clothes, shoes, electronics, etc)? Your assignment would then reduce to a simple WHERE on this ProductCategory field.
An equivalent (albeit less efficient) view can be achieved without modifying your schema by UNION-ing all 10 tables together, upon which you can draw your assignment.
I realise this does not directly answer your question, but this would drastically simplify your problem and normalise your database, thereby eliminating the need for a complicated solution (which is always the preferred way to go).

I think you should go for pipe lined function with dynamic sql.
e.g. you will create sql on the fly, execute it with execute immediate, return resuls row by row. Take a look here.
Note that there is no accepted answer, however I think
Vincent Malgrat's
approach will do just fine.

Related

SQL IN statement with multiple conditions and 3 tables

I have just learned IN and NOT IN statements in SQL. I understand statements like
SELECT * FROM table1 WHERE table1.attribute IN
(SELECT table2.attribute FROM table2 WHERE attribute = condition);
My problem is I need to show 3 columns (from two different tables) using an IN (SELECT) statement with a 3rd table with a condition from that 3rd table only and another condition from one of the first two tables.
Example:
Show name(table1), class(table1), course(table2) where course LIKE "cosc%" (table 2) and grade = 'b' (table 3). I need to USE an IN statement.
My attempt:
SELECT stud.sname, stud.class, sec.course_num FROM stud, sec
WHERE sec.course_num LIKE 'COSC%'
AND stud.stno IN
(SELECT gr.student_number FROM gr WHERE gr.grade = 'B');
Table3 shares a domain with both table1 and table2. This query resulted in way too many rows, there are only 48 rows in one of these tables. I know how to do all the JOIN statements, I don't need that.
Something like this, assuming stud refers to sec:
SELECT stud.sname, stud.class, sec.course_num
FROM stud JOIN sec ON stud.sec_id = sec.id
WHERE sec.course_num LIKE 'COSC%'
AND stud.stno IN (
SELECT gr.student_number FROM gr WHERE gr.grade = 'B'
)
;
It's possible the logic needs to be adjusted and that other logic is missing.
We'll see when the question is updated with the requested detail.

Values after join are incorrect

I have 2 database tables. Table A has to fetch some records based on parameter passed there may or may not be an entry in table B with that key.
What I want to do is:
select a.col1,a.col2,a.col3
FROM table WHERE a.id = 123
This would fetch 20 rows. For one of the rows there is an entry in another table B.
select T_level from table b where b.id = 123
only one record appears with right value.
What I want is to get this in a single query. Something like:
select a.col1,a.col2,a.col3,b.T_level
from a,b
where a.id = 123
and a.id = b.id
When I do that, I get 20 rows and the column T_level as '50' for all the rows, whereas it should be '50' for one correct row, for rest it should be null.
I further tried:
select a.col1,a.col2,a.col3,nvl(b.T_level,0) from a,b
but that doesn't fetch the way I expect.
Firstly, please learn to use ansi sql join syntax. The Oracle join syntax you are using hasn't been considered good practice for decades
SQL Join syntax
If you want to get all records from a and any matching records from b then you need to use a LEFT OUTER JOIN

SQL Query returns more

I'm having a bit of a problem with a SQL Query that returns too many results. I'm fairly new to SQL so please bear with me.
Please see the following:
Table Structures
The Query that I use looks like:
SELECT TABLE_B.*
FROM
TABLE_A
JOIN
TABLE_B
ON
TABLE_A.COMMON_ID=TABLE_B.COMMON_ID
AND TABLE_A.SEQ_3C=TABLE_B.SEQ_3C
JOIN
TABLE_C
ON
TABLE_A.COMMON_ID=TABLE_C.EMPLID
WHERE
TABLE_B.ITEM_STATUS<>'C'
and TABLE_A.CHECKLIST_STATUS='I'
and TABLE_A.ADMIN_FUNCTION='ADMA'
and TABLE_A.CHECKLIST_CD='APPL'
and TABLE_A.COMMON_ID = '123456789'
and TABLE_C.ADMIT_TERM='2171'
and TABLE_C.INSTITUTION='SOMEWHERE'
I just want the results from Table_B and not what it's giving me.
Please explain this to me as I have spent 3 days on it non-stop.
What am I missing?
You want data from TABLE_B? Then select from it only and have the conditions on the other tables in your where clause.
The inner joins on the other tables serve as existence tests, I assume? Don't do that. You'd only multiply your records, just as you are doing now, only to have to dismiss duplicates later. That can cause bad performance on large tables and errors in more complicated queries. Use EXISTS or IN instead.
select *
from table_b
where item_status <> 'C'
and (common_id, seq_3c) in
(
select common_id, seq_3c
from table_a
where checklist_status = 'I'
and admin_function = 'ADMA'
and checklist_cd = 'APPL'
)
and common_id in
(
select EMPLID
from table_c
where admit_term = '2171'
and institution = 'SOMEWHERE'
);
SELECT DISTINCT TABLE_B.*
FROM
TABLE_A
JOIN
TABLE_B
ON
TABLE_A.COMMON_ID=TABLE_B.COMMON_ID
AND TABLE_A.SEQ_3C=TABLE_B.SEQ_3C
JOIN
TABLE_C
ON
TABLE_A.COMMON_ID=TABLE_C.EMPLID
WHERE
TABLE_B.ITEM_STATUS<>'C'
and TABLE_A.CHECKLIST_STATUS='I'
and TABLE_A.ADMIN_FUNCTION='ADMA'
and TABLE_A.CHECKLIST_CD='APPL'
and TABLE_A.COMMON_ID = '123456789'
and TABLE_C.ADMIT_TERM='2171'
and TABLE_C.INSTITUTION='SOMEWHERE'
This should be easy to understand without looking at all your tables and output.
Suppose you join two tables, A and B, on a column id. You only want the columns from table B, and in table B the `id' column is a unique identifier.
Even so, if in table A an id (the same id) appears five times, the join will have five rows for that id. Then you just select the columns from table B, so it will look like you got the same row five different times.
Perhaps you don't really need a join? What is your underlying problem you are trying to solve?
It's hard to answer this question without more information about why you're executing these joins. I can explain why you're getting the results you're getting, and hopefully that will allow you to solve the problem yourself.
You start, in your FROM clause, with table A. You join this table with table B on matching COMMON_ID, which, based on the tables you provide, returns three matches for the one record you have in table A. This increases your result set size to three records. Next, you join these three records with table C, on matching ID. Because all ID's are, in fact, identical, this returns nine matches for every record in your current result set: you now have 9 x 3 = 27 records in your result set.
Finally, the WHERE clause comes into effect. This clause excludes 6 out of 9 records in table C, so you have 3 of those records left. Your final result set is therefore 1 (table A) x 3 (table B) x 3 (table C) = 9 records.

SQL Join between tables with conditions

I'm thinking about which should be the best way (considering the execution time) of doing a join between 2 or more tables with some conditions. I got these three ways:
FIRST WAY:
select * from
TABLE A inner join TABLE B on A.KEY = B.KEY
where
B.PARAM=VALUE
SECOND WAY
select * from
TABLE A inner join TABLE B on A.KEY = B.KEY
and B.PARAM=VALUE
THIRD WAY
select * from
TABLE A inner join (Select * from TABLE B where B.PARAM=VALUE) J ON A.KEY=J.KEY
Consider that tables have more than 1 milion of rows.
What your opinion? Which should be the right way, if exists?
Usually putting the condition in where clause or join condition has no noticeable differences in inner joins.
If you are using outer joins ,putting the condition in the where clause improves query time because when you use condition in the where clause of
left outer joins, rows which aren't met the condition will be deleted from the result set and the result set becomes smaller.
But if you use the condition in join clause of left outer joins ,no rows deletes and result set is bigger in comparison to using condition in the where clause.
for more clarification,follow the example.
create table A
(
ano NUMBER,
aname VARCHAR2(10),
rdate DATE
)
----A data
insert into A
select 1,'Amand',to_date('20130101','yyyymmdd') from dual;
commit;
insert into A
select 2,'Alex',to_date('20130101','yyyymmdd') from dual;
commit;
insert into A
select 3,'Angel',to_date('20130201','yyyymmdd') from dual;
commit;
create table B
(
bno NUMBER,
bname VARCHAR2(10),
rdate DATE
)
insert into B
select 3,'BOB',to_date('20130201','yyyymmdd') from dual;
commit;
insert into B
select 2,'Br',to_date('20130101','yyyymmdd') from dual;
commit;
insert into B
select 1,'Bn',to_date('20130101','yyyymmdd') from dual;
commit;
first of all we have normal query which joins 2 tables with each other:
select * from a inner join b on a.ano=b.bno
the result set has 3 records.
now please run below queries:
select * from a inner join b on a.ano=b.bno and a.rdate=to_date('20130101','yyyymmdd')
select * from a inner join b on a.ano=b.bno where a.rdate=to_date('20130101','yyyymmdd')
as you see above results row counts have no differences,and According to my experience there is no noticeable performance differences for data in large volume.
please run below queries:
select * from a left outer join b on a.ano=b.bno and a.rdate=to_date('20130101','yyyymmdd')
in this case,the count of output records will be equal to table A records.
select * from a left outer join b on a.ano=b.bno where a.rdate=to_date('20130101','yyyymmdd')
in this case , records of A which didn't met the condition deleted from the result set and as I said the result set will have less records(in this case 2 records).
According to above examples we can have following conclusions:
1-in case of using inner joins,
there is no special differences between putting condition in where clause or join clause ,but please try to put tables in from clause in order to have minimum intermediate result row counts:
(http://www.dba-oracle.com/art_dbazine_oracle10g_dynamic_sampling_hint.htm)
2-In case of using outer joins,whenever you don't care of exact result row counts (don't care of missing records of table A which have no paired records in table B and fields of table B will be null for these records in the result set),put the condition in the where clause to delete a set of rows which aren't met the condition and obviously improve query time by decreasing the result row counts.
but in special cases you HAVE TO put the condition in the join part.for example if you want that your result row count will be equal to table 'A' row counts(this case is common in ETL processes) you HAVE TO put the condition in the join clause.
3-avoiding subquery is recommended by lots of reliable resources and expert programmers.It usually increase the query time and you can use subquery just when its result data set is small.
I hope this will be useful:)
1M rows really isn't that much - especially if you have sensible indexes. I'd start off with making your queries as readable and maintainable as possible, and only start optimizing if you notice a perforamnce problem with the query (and as Gordon Linoff said in his comment - it's doubtful there would even be a difference between the three).
It may be a matter of taste, but to me, the third way seems clumsy, so I'd cross it out. Personally, I prefer using JOIN syntax for the joining logic (i.e., how A and B's rows are matched) and WHERE for filtering (i.e., once matched, which rows interest me), so I'd go for the first way. But again, it really boils down to personal taste and preferences.
You need to look at the execution plans for the queries to judge which is the most computationally efficient. As pointed out in the comments you may find they are equivalent. Here is some information on Oracle execution plans. Depending on what editor / IDE you use the may be a shortcut for this e.g. F5 in PL/SQL Developer.

Setting up database structure

I am trying to figure out the best way to restructure my database as I didn't plan ahead and now I am a little stuck on this part :)
I have a Table called Campaigns and a Table called Data Types.
Each campaign is a unique record that holds about 10 fields of data.
The data types contains 3 fields - ID, Type, Description
When You create a campaign, you can select as many data types as you would like.
1, 2 or all 3 of them.
My concern / question is - How can I store what the user selected with the campaign record?
I need to be able to pull in the campaign details but also know which data types were selected.
How I originally had it set up was the data types were in 1 field, comma separated but learned is not ideal to do that.
What would be the best way to accomplish this? Storing the data as XML ?
UPDATE -
Here is an example of the query I was trying to get to work (its probably way off).
BEGIN
SET NOCOUNT ON;
BEGIN
SELECT *
FROM (SELECT A.[campaignID] as campaignID,
A.[campaignTitle],
A.[campaignDesc],
A.[campaignType],
A.[campaignStatus],
A.[duration],
A.[whoCreated],
B.[campaignID],
B.[dataType],
(SELECT *
FROM Tags_Campaign_Settings
WHERE campaignID = #campaignID) AS dataTypes
FROM Tags_Campaigns AS A
INNER JOIN
Tags_Campaign_Settings AS B
ON A.[campaignID] = B.[campaignID]
WHERE A.[campaignID] = #campaignID
) AS a
FOR XML PATH ('campaigns'), TYPE, ELEMENTS, ROOT ('root');
END
END
Create a join table called Campain_DataType with campaignId and dataTypeId. Make sure they're foreign key constrained to the respective tables. When you query for campaign data, you can either create a separate query to get the data type information based on the campaignId, or you can do a left outer join to fetch campaigns and their data types together.
If you want to collapse the 3 data types into the same row, then give the following a shot. It's definitely on the hacky side, and it'll only work with a fixed number of data types. If you add another data type, you'll have to update this query to support it.
SELECT
Campaign.ID,
Campaign.foo,
Campaign.bar,
dataType1.hasDataType1,
dataType2.hasDataType2,
dataType3.hasDataType3
FROM
Campaign
LEFT OUTER JOIN
( SELECT
1 as hasDataType1,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type1'
) dataType1 ON dataType1.campaignID = Campaign.ID
LEFT OUTER JOIN
( SELECT
1 as hasDataType2,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type2'
) dataType2 ON dataType2.campaignID = Campaign.ID
LEFT OUTER JOIN
( SELECT
1 as hasDataType3,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type3'
) dataType3 ON dataType3.campaignID = Campaign.ID
The record you receive for each Campaign will have three fields: hasDataType1, hasDataType2, hasDataType3. These columns will be 1 for yes, NULL for no.
Looks to me like what you want here is a crosstab query. Take a look at:
Sql Server 2008 Cross Tab Query