Join Query in Oracle Xpress Edition - inner-join

Hello I am creating a table with name Category, inserted two rows.
When I am joining this table, output printed multiple rows.
My Table is:- Category
CREATE TABLE Category(
ID int NOT NULL Primary key,
Code varchar(50),
Name varchar(200),
Notes varchar(500),
Image1 varchar(250),
Image2 varchar(250),
Image3 varchar(250),
CreatedBy int NOT NULL,
PageTitle varchar(100),
MetaName varchar(200),
MetaDescription varchar(500),
MetaKeywords varchar(500),
ERPReferencesSysteID int,
ERPReferencesID int,
AssignedTo int,
Status int );
Insert Query:-
insert into Category values (902,'Code2', 'NAME2','Note2', 'Image12',
'Image22','Image32', 2016, 'PageTitle2','MetaName2', 'MetaDescription2',
'MetaKeyword2',02, 08, 2,1);
My Join Query:-
SELECT DISTINCT info.COLUMN_NAME AS NAME, info.OWNER AS TABLESCHEMA,
info.TABLE_NAME,UIC.COLUMN_POSITION AS POSITION,info.NULLABLE AS ISNULLABLE,
info.DATA_TYPE AS DATATYPE,tc.CONSTRAINT_TYPE AS CONSTRAINTTYPE,
tc.R_OWNER AS REFERENCESCHEMA FROM all_tab_columns
info join all_constraints tc on info.TABLE_NAME = tc.TABLE_NAME
join user_cons_columns rk on info.TABLE_NAME = rk.TABLE_NAME
join USER_IND_COLUMNS UIC on UIC.TABLE_NAME = info.TABLE_NAME
where info.TABLE_NAME= 'CATEGORY' AND info.owner ='SAN' ORDER BY
UIC.COLUMN_POSITION ASC;
When I am executing this join query rows are showing multiple times.
What i am doing wrong in this query?

Use INNER JOIN's for joining your tables.
SELECT DISTINCT info.COLUMN_NAME AS NAME, info.OWNER AS TABLESCHEMA,
info.TABLE_NAME,UIC.COLUMN_POSITION AS POSITION,info.NULLABLE AS ISNULLABLE,
info.DATA_TYPE AS DATATYPE,tc.CONSTRAINT_TYPE AS CONSTRAINTTYPE,
tc.R_OWNER AS REFERENCESCHEMA
FROM all_tab_columns info
INNER JOIN all_constraints tc ON info.TABLE_NAME = tc.TABLE_NAME
INNER JOIN user_cons_columns rk ON info.TABLE_NAME = rk.TABLE_NAME
INNER JOIN USER_IND_COLUMNS UIC ON UIC.TABLE_NAME = info.TABLE_NAME
WHERE info.TABLE_NAME = 'CATEGORY'
AND info.owner ='SAN'
ORDER BY UIC.COLUMN_POSITION ASC;

You have three constraints in the table; the primary key and two not-null constraints. One of those is redundant as the primary key column is implicitly not null anyway.
So there are three rows in user_cons_columns. Two of those are for id.
SELECT uc.constraint_name, uc.constraint_type, ucc.column_name
FROM user_constraints uc
JOIN user_cons_columns ucc ON ucc.constraint_name = uc.constraint_name
WHERE uc.table_name = 'CATEGORY';
CONSTRAINT_NAME C COLUMN_NAME
------------------------------ - --------------------
SYS_C00110620 C ID
SYS_C00110621 C CREATEDBY
SYS_C00110622 P ID
There is also one row in user_ind_columns for id, but not one for createdby, which is the other not-null constraint.
SELECT ui.index_name, uic.column_name
FROM user_indexes ui
JOIN user_ind_columns uic ON uic.index_name = ui.index_name
WHERE ui.table_name = 'CATEGORY';
INDEX_NAME COLUMN_NAME
------------------------------ --------------------
SYS_C00110622 ID
Both the id rows in user_cons_columns are matching with the one in user_ind_columns, so you get two rows in the result set. The distinct isn't helping because you are including the constraint type in the result set, so you see separate rows for P and C.
You are also not including the column_name in the joins; so all 16 row in all_tab_columns are joining to all three constraints and their columns. As you are using inner joins you seem to only want to show the primary key and its index, so this seems to be a long-winded way to do that:
SELECT distinct atc.column_name AS NAME, atc.owner AS tableschema,
atc.table_name, aic.column_position AS position, atc.nullable AS isnullable,
atc.data_type AS datatype, ac.constraint_type AS constrainttype,
ac.r_owner AS referenceschema
FROM all_tab_columns atc
JOIN all_constraints ac
ON ac.owner = atc.owner
AND ac.table_name = atc.table_name
JOIN all_cons_columns acc
ON acc.table_name = atc.table_name
AND acc.column_name = atc.column_name
JOIN all_ind_columns aic
ON aic.table_owner = atc.owner
AND aic.table_name = atc.table_name
AND aic.column_name = atc.column_name
WHERE atc.table_name= 'CATEGORY'
AND atc.owner = 'SAN'
AND ac.constraint_type = 'P'
ORDER BY aic.column_position ASC;
Which gets a single row for the P constraint ID column. If you intended to show all the columns you'd need outer joins.

Related

Optimizing query with huge amount of data

How can I optimize the query. I looked at the execution plan and created all the index. Every table has huge data. And this query execution time is very large. By looking at the query could you please suggest where can I optimize more.
If I give little background of the query the structure like:
There are many companies
Each company can have multiple managers
Data is in pagination format
Filter on #parent_manager so another temp table created parent_manager_filter just to use for the filtering purpose as #parent_manager has name in "," separated format
CREATE TABLE #parent_manager
(
cid NUMERIC(18) PRIMARY KEY,
name NVARCHAR(MAX),
code NVARCHAR(MAX)
);
CREATE INDEX cte_parent_manager ON #parent_manager(cid);
CREATE TABLE #parent_manager_filter
(
cid NUMERIC(18),
name NVARCHAR(1000),
code NVARCHAR(1000)
);
CREATE INDEX cte_parent_manager_filter_idx ON #parent_manager_filter(cid);
INSERT INTO #parent_manager
SELECT DISTINCT
mgrc.cid,
name = CAST (STUFF ((SELECT ', ' + CAST(c.company_name AS varchar(2000))
FROM manager_company mc
INNER JOIN company c ON (mc.mgr_cid = c.cid )
WHERE mc.cid = mgrc.cid
AND c.company_name IS NOT NULL
FOR XML PATH ('')), 1, 1, '') AS VARCHAR(2000)),
code = CAST (STUFF ((SELECT ', ' + CAST(c.code AS varchar(2000))
FROM manager_company mc
INNER JOIN company c ON (mc.mgr_cid = c.cid )
WHERE mc.cid = mgrc.cid
AND c.company_name IS NOT NULL
FOR XML PATH ('')), 1, 1, '') AS VARCHAR(2000))
FROM
manager_company mgrc
INNER JOIN
company c ON (mgrc.mgr_cid = c.cid )
JOIN
handler h ON (c.handlerId = h.handlerid )
WHERE
h.handlerid = 5800657002370
INSERT INTO #parent_manager_filter
SELECT DISTINCT
mc.cid,
c.company_name as name,
c.code as code
FROM
manager_company mc
INNER JOIN
company c ON (mc.mgr_cid = c.cid )
JOIN
handler h ON (h.handlerid = c.handlerid)
WHERE
h.handlerid = 5800657002370 ;
WITH company AS
(
SELECT DISTINCT
c.cid AS cid,
parentManager.name AS MANAGER_NAME,
parentManager.code AS code
FROM
company c
LEFT JOIN
#parent_manager parentManager ON (parentManager.cid = c.cid)
LEFT JOIN
# parent_manager_filter parentManagerFilter ON (parentManagerFilter.cid = c.cid)
WHERE
parentManagerFilter.name IN (:managerList)
),
total_rows AS
(
SELECT
COUNT(*) OVER () AS TOTALCOUNT,
ROW_NUMBER() OVER (ORDER BY company_name ASC) AS rnum,
grid.*
FROM
company grid
)
SELECT *
FROM total_rows rnum
WHERE rnum >= 1
AND rnum <= 10
DROP TABLE #parent_manager;
DROP TABLE #parent_manager_filter;
If you are building up temp tables then I would make sure you don't miss a clustered index, else your temp table is simply a heap. You don't have one covering the filter table.
INSERT INTO #parent_manager_filter ...
CREATE CLUSTERED INDEX cte_parent_manager_filter On #parent_manager_filter(cid);

Why joining same table twice returns different column values?

I have a stored procedure that was not written by me.
I cannot understand how they are joining the same table tblUsers twice and it returns different column values:
select distinct
usr.Name_FirstLast AS AssignedTo,
usr1.Name_FirstLast as AssignedBy
from
dbo.tblNoteStore nt_str
join
dbo.tblNoteEntities entit ON nt_str.ID = entit.NoteGUID
join
dbo.tblNoteDiaries nt_dia ON nt_str.ID = nt_dia.NoteGUID
join
dbo.tblNoteEntries entri on nt_str.ID = entri.NoteGUID
and nt_dia.EntryGUID = entri.ID
and entit.NoteGUID = entri.NoteGUID
left join
dbo.tblNoteRecipients recip ON entri.ID = recip.EntryGUID
--this is where the same table used twice
left join
dbo.tblUsers usr ON recip.UserGUID = usr.UserGUID -- returns AssignedTo column
left join
dbo.tblUsers usr1 ON usr1.UserGuid = entri.UserGUID -- returns AssignedBy column
where
usr.UserGUID = '55610B2F-1997-40C0-9F01-EED3ED2939F9'
The result is what I need, but why it happens that way?
dbo.tblUsers has primary key UserGUID
dbo.tblNoteEntries has a foreign key UserGUID
dbo.tblNoteRecipients has a foreign key UserGUID
Also, do I have to use all those tables in JOIN in order to receive the result?
LEFT JOIN dbo.tblUsers usr ON recip.UserGUID = usr.UserGUID -- > gives me AssignedTo column
LEFT JOIN dbo.tblUsers usr1 ON usr1.UserGuid = entri.UserGUID--gives me AssignedBy column
The UserGUID column of the 2 dbo.tblUsers table references are correlated using different columns (recip.UserGUID and entri.UserGUID) so different rows from dbo.tblUsers may be returned.

SQL Join - two foreign keys in the same table reference a primary key

I have two tables, one is a product transforming history table and the other is the products table:
dbo.Product_Change_History
Date FK_ProductID FK_FinalProductID ChangedBy
20160709 1 3 John
dbo.Product_Detail
PK_ProductID ProductName
1 Red Bike
3 Green Bike
I would like a table like:
Date ProductName FinalProductName
20160709 Red Bike Green Bike
How do I join the two tables to achieve this?
edit: This is in TSQL
You'd have to join twice, which means you to alias at least one of the joins:
SELECT Date, foo.ProductName, bar.FinalProductName
FROM Product_Change_history
LEFT JOIN Product_Detail AS foo ON fk_productid = foo.pk_productid
LEFT JOIN Product_Detail AS bar ON fk_finalproductid = bar.pk_productid
I'm not sure about LEFT joins VS INNER. I would think this would work well.
SELECT
link.Date,
old.ProductName,
new.ProductName as FinalProductName
FROM
dbo.Product_Detail as old
INNER JOIN dbo.Product_Change_History as link
ON old.PK_ProductID = link.FK_ProductID
INNER JOIN dbo.Product_Detail as new
ON new.PK_ProductID = link.FK_FinalProductID
declare #ProdChange table(ProductID int, FK_ProductID INT, FK_FinalProductID INT, ChangedBy Varchar(100))
declare #Prod table(ProductID int, ProductName Varchar(100))
insert into #Prod Values(1,'Red Bike')
insert into #Prod Values(3,'Green Bike')
insert into #ProdChange Values(1,1,3,'John')
select * from #Prod
select * from #ProdChange
SELECT
old.ProductName,
new.ProductName as FinalProductName
FROM
#Prod as old
INNER JOIN #ProdChange as link
ON old.ProductID = link.FK_ProductID
INNER JOIN #Prod as new
ON new.ProductID = link.FK_FinalProductID
Thank you for the responses, with the help Chad Zinks answer I came to the correct solution:
select
PRDHIST.Date,
OLDPROD.ProductName,
NEWPROD.ProductName
from dbo.Product_ChangeHistory PRDHIST
inner join dbo.Product_Detail OLDPROD
on OLDPROD.PK_ProductID = PRDHIST.FK_ProductID
inner join dbo.Product_Detail NEWPROD
on NEWPROD.PK_ProductID = PRDHIST.FK_FinalProductID;

Counting grouped records from multiple tables

there is a column comment_id of a table called pic_alb_love which i'd like to add to the query below but i don't know how. Actually what i want to do is to count the total comment_id of the table pic_alb_love.
SELECT users_pics.wardrobe,
profile.fname,
users_pics.pic,
users_pics.u_pic_id,
users_pics.email,
users_pics.make,
users_pics.designer,
photo_comment.comment,
max_photo_comment.count_pic_id
FROM dbo.users_pics
INNER JOIN profile
ON users_pics.email = profile.email
LEFT Join (SELECT pic_id
,MAX(comment_id) max_comment_id
,COUNT(pic_id) count_pic_id
FROM photo_comment
GROUP BY pic_id
) max_photo_comment
On users_pics.u_pic_id = max_photo_comment.pic_id
LEFT Join photo_comment
On max_photo_comment.pic_id = photo_comment.pic_id
AND max_photo_comment.max_comment_id = photo_comment.comment_id
WHERE users_pics.wardrobe = MMColParam
AND users_pics.email = MMColParam2
ORDER BY u_pic_id asc
these are the various fields of the table pic_alb_love:
(comment_id,pic,love_com, wardrobe, email
,com_name,resp_email, play_count, com_stat)
LEFT JOIN (SELECT Pic
,Count(*) [CommentCount]
FROM pic_alb_love
GROUP BY Pic) c
ON c.Pic=u_pic_id
Assuming that pic_alb_love.pic is the FK on the table...
Use [CommentCount] in the select list.

T-SQL: Same fields using multiple joins

I reviewed similar questions but could not find an answer to my specific quandry. I'm working with SQL Server 2008 (T-SQL in SQL Server Management Studio) (but much more used to Oracle and Crystal Reports).
Simplified scenario:
Table Customer
customerID (pk)...
Table InsuranceCoverage
customerID (composite pk)
line (composite pk)
insCompanyID (fk)
insPlanID (fk)
Table InsuranceCompany
insCompanyID
insCompanyName
insCompanyAddr
Table InsurancePlan
insPlanID
insPlanName
insPlanClass
I need a report that basically returns the following on one row:
A few columns from Customer
Insurance 1 - columns from InsuranceCompany and InsurancePlan tables where InsuranceCoverage.line = 1
Insurance 2 - columns from InsuranceCompany and InsurancePlan tables where InsuranceCoverage.line = 2
Insurance 3 - columns from InsuranceCompany and InsurancePlan tables where InsuranceCoverage.line = 3
I feel very stupid not being able to figure this out. One customer might have up to three insurances. This would be easy to write multiple queries for, but I've got to get it set up so it can run automatically 1x/month. I've used the same table multiple times in the same report by using aliases on the joins before, but that won't work here because of the InsuranceCoverage.line criteria, right? Is a subquery in the from clause the answer?
Something like this?
SELECT
c.CustomerID,
cov1.*,
cov2.*,
cov3.*,
insco1.insCompanyName as insCompanyName1,
insco2.insCompanyName as insCompanyName2,
insco3.insCompanyName as insCompanyName3,
etc...
FROM
Customer c
LEFT OUTER JOIN InsuranceCoverage cov1 on cov1.CustomerID = c.CustomerID AND cov1.line = 1
LEFT OUTER JOIN InsuranceCoverage cov2 on cov2.CustomerID = c.CustomerID AND cov2.line = 2
LEFT OUTER JOIN InsuranceCoverage cov3 on cov3.CustomerID = c.CustomerID AND cov3.line = 3
JOIN InsuranceCompany insco1 on insco1.insCompanyID = cov1.insCompanyID
JOIN InsuranceCompany insco2 on insco2.insCompanyID = cov2.insCompanyID
JOIN InsuranceCompany insco3 on insco3.insCompanyID = cov3.insCompanyID
JOIN InsurancePlan inspl1 on inspl1.insPlanID = cov1.insPlanID
JOIN InsurancePlan inspl2 on inspl2.insPlanID = cov2.insPlanID
JOIN InsurancePlan inspl3 on inspl3.insPlanID = cov3.insPlanID
I set up some table variables to show that this query works. You'll need to replace them with the real table and column names. I believe something like this would work for you:
DECLARE #Customer TABLE (CustomerId INT)
DECLARE #InsuranceCoverage TABLE
(
CustomerId INT
, Line INT
, InsuranceCompanyId INT
, InsurancePlanId INT
)
DECLARE #InsuranceCompany TABLE
(
Id INT
, Name VARCHAR(100)
, Addr VARCHAR(100)
)
DECLARE #InsurancePlan TABLE
(
Id INT
, Name VARCHAR(100)
, Class VARCHAR(100)
)
SELECT
C.* -- Customer colums.
-- Insurance1 columns.
, ICmp1.*
, IP1.*
-- Insurance2 columns.
, ICmp2.*
, IP2.*
-- Insurance3 columns.
, ICmp3.*
, IP3.*
FROM
#Customer C
LEFT JOIN #InsuranceCoverage ICov1
INNER JOIN #InsuranceCompany ICmp1
ON ICmp1.Id = ICov1.InsuranceCompanyId
INNER JOIN #InsurancePlan IP1
ON IP1.Id = ICov1.InsurancePlanId
ON ICov1.CustomerId = C.CustomerId
AND ICov1.Line = 1
LEFT JOIN #InsuranceCoverage ICov2
INNER JOIN #InsuranceCompany ICmp2
ON ICmp2.Id = ICov2.InsuranceCompanyId
INNER JOIN #InsurancePlan IP2
ON IP2.Id = ICov2.InsurancePlanId
ON ICov2.CustomerId = C.CustomerId
AND ICov2.Line = 2
LEFT JOIN #InsuranceCoverage ICov3
INNER JOIN #InsuranceCompany ICmp3
ON ICmp3.Id = ICov3.InsuranceCompanyId
INNER JOIN #InsurancePlan IP3
ON IP3.Id = ICov3.InsurancePlanId
ON ICov3.CustomerId = C.CustomerId
AND ICov3.Line = 3
you can do a union of 2, 3, 4 as a derived table and join 1 with that, like...
select t1.a,t1.b,t1.c, t2.d, t2.e, t2.f from customer t1,
(select fk, d as d, 0 as e, 0 as f from ic where line=1
union select fk, 0, e, 0 from ic where line=2
union select fk, 0, 0, f from ic where line=3) as t2
where t1.pk = t2. fk
something like that
edit: oh, right, if they can have no insurance then change it to a left join like ...
select t1.a,t1.b,t1.c, t2.d, t2.e, t2.f from customer t1 left join
(select fk, d as d, 0 as e, 0 as f from ic where line=1
union select fk, 0, e, 0 from ic where line=2
union select fk, 0, 0, f from ic where line=3) as t2
on t1.pk = t2.fk
... or something like that :)

Resources