SQL join and Bring in 1 column into 2 columns - sql

I have two tables shown as above. I want to create a select statement that would have the following result:
Basically, I want to join on 'Model' and 'Num' columns and bring 'Val' vales but break it into two columns based on 'Ver'.
SELECT 'a' as Model ,'1' as Num ,'v1' as Ver ,'9' as val INTO #RefTbl UNION ALL
SELECT 'a','2','v1','10' UNION ALL
SELECT 'a','3','v1','11' UNION ALL
SELECT 'a','1','v2','5' UNION ALL
SELECT 'a','2','v2','6' UNION ALL
SELECT 'a','3','v2','7' UNION ALL
SELECT 'b','1','v1','20' UNION ALL
SELECT 'b','1','v2','21' UNION ALL
SELECT 'b','2','v1','25' UNION ALL
SELECT 'b','2','v2','26'
SELECT '519' as ID,'a' as Model,'1' as Num INTO #OrderTbl UNION ALL
SELECT '5616','a','3' UNION ALL
SELECT '871','b','1'
-- failed attempt
SELECT o.*, '' as v1_val, '' as v2_val FROM #OrderTbl as o left join #RefTbl as r on o.Model = r.Model and o.Num = r.Num

You can join and do conditional aggregation:
select
d.ID,
d.Model,
d.Num,
max(case when r.Ver = 'val1' then Val end) V1_Val,
max(case when r.Ver = 'val2' then Val end) V2_Val
from DataTbl d
inner join ReferenceTbl r
on r.Model = d.Model
and r.Num = d.Num
group by
d.ID,
d.Model,
d.Num
Alternatively, you can join twice - depending on your dataset, this might (or might not) perform better:
select
d.ID,
d.Model,
d.Num,
r1.Val V1_Val,
r2.Val V2_Val
from DataTbl d
left join ReferenceTbl r1
on r1.Model = d.Model
and r1.Num = d.Num
and r1.Ver = 'val1'
left join ReferenceTbl r2
on r2.Model = d.Model
and r2.Num = d.Num
and r2.Ver = 'val2'

Case + Group by works on most db system which is nice.
Alternative answer using SQL Server PIVOT (for reference)
WITH RefTbl AS (
SELECT 'a' as Model ,'1' as Num ,'v1' as Ver ,'9' as val UNION ALL
SELECT 'a','2','v1','10' UNION ALL
SELECT 'a','3','v1','11' UNION ALL
SELECT 'a','1','v2','5' UNION ALL
SELECT 'a','2','v2','6' UNION ALL
SELECT 'a','3','v2','7' UNION ALL
SELECT 'b','1','v1','20' UNION ALL
SELECT 'b','1','v2','21' UNION ALL
SELECT 'b','2','v1','25' UNION ALL
SELECT 'b','2','v2','26'),
OrderTbl AS (
SELECT '519' as ID,'a' as Model,'1' as Num UNION ALL
SELECT '5616','a','3' UNION ALL
SELECT '871','b','1'
)
SELECT Id, Model, Num AS Model, [v1] AS v1_Val, [v2] as v2_Val
FROM
(SELECT o.id, r.Model, r.Num, r.val, r.ver from OrderTbl as o
left join RefTbl as r on o.Model = r.Model and o.Num = r.Num) AS SourceTable
PIVOT
(MAX(val) FOR ver IN ([v1], [v2])) AS PivotTable;
Id Model Model v1_Val v2_Val
519 a 1 9 5
5616 a 3 11 7
871 b 1 20 21

Related

How to optimize this UNION SQL Query?

I have a query which looks like this:
SELECT DISTINCT * FROM (
SELECT FundId AS Id,
PeriodYearMonth
FROM [Fund.Period] F
INNER JOIN (
SELECT * FROM (
SELECT FundId as Id,
MIN(PeriodYearMonth) AS MinPeriodYearMonth
FROM (
SELECT FundId,
PeriodYearMonth,
PublishedOn
FROM [Fund.Period] FP
UNION ALL --Changed to UNION ALL as it is more efficient and we wont ever need a UNION as the result set would never match
SELECT FundId,
MAX(PeriodYearMonth) + 1,
NULL
FROM [Fund.Period]
GROUP BY FundId
) FP WHERE PublishedOn IS NULL GROUP BY FundId
) MFP
) FP ON F.FundId = FP.Id AND (F.PeriodYearMonth = FP.MinPeriodYearMonth OR (f.PeriodYearMonth +1) = FP.MinPeriodYearMonth)
) FP
If possible, I would like to remove the UNION ALL. Does anyone know how this can be optimized?
removed union all
SELECT DISTINCT * FROM (
SELECT FundId AS Id,
PeriodYearMonth
FROM [Fund.Period] F
INNER JOIN (
SELECT * FROM (
SELECT FundId as Id,
MIN(PeriodYearMonth) AS MinPeriodYearMonth,MAX(PeriodYearMonth) + 1 as PeriodYearMonth
FROM (
SELECT FundId,
PeriodYearMonth,
PublishedOn
FROM [Fund.Period] FP
) FP WHERE PublishedOn IS NULL
GROUP BY FundId
) MFP
) FP ON F.FundId = FP.Id AND (F.PeriodYearMonth = FP.MinPeriodYearMonth OR (f.PeriodYearMonth +1) = FP.MinPeriodYearMonth)
) FP

How to get only one value when you are getting multiple values against key in SQL

I have a SQL query. Below is the query
select
ID,
replace(replace(replace(replace([Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace([Description],',',''),'"','') [Description],
[GUID],
Bussinesskey
from course
where COURSESTATUS = 'Published' and RETIREDTF = 0
and
bussinesskey in
(
...
)
and id in (
select c.id from course c
inner join COURSE_CUSTOMERENTITLEMENT cce on cce.COURSE_ID = c.ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cce.CUSTOMERENTITLEMENT_ID
where
ce.ENROLLMENTTYPE = 'Course'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
UNION
select c.id from course c
inner join COURSE_COURSEGROUP cg on cg.course_id = c.id
inner join COURSEGROUP_CUSTOMERENTITLEMENT cgce on cgce.COURSEGROUP_ID = cg.COURSEGROUP_ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cgce.CUSTOMERENTITLEMENT_ID
where
ce.ENROLLMENTTYPE = 'CourseGroup'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
)
order by name, id asc
When this query runs then I get the output like the following snapshot
You can see in the screen shot that I am getting 8 names of same type(Contracts). The last id of Contracts is 780697 which is the latest record that is added to database. Now i want that when my query runs then it gets only the latest record. Means instead of showing 8 name of Contarcts. Its only the show the latest one for each course name. Means for Contracts only record with ID 780697 is shown. If other courses has the same result then there latest Id record is shown only. How can I achieve this ?
Thanks
You can try following for achieving latest ID:-
select MAX(ID),
replace(replace(replace(replace([Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace([Description],',',''),'"','') [Description],
[GUID],
Bussinesskey
from course
where COURSESTATUS = 'Published' and RETIREDTF = 0
and
bussinesskey in
(
...
)
and id in (
select c.id from course c
inner join COURSE_CUSTOMERENTITLEMENT cce on cce.COURSE_ID = c.ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cce.CUSTOMERENTITLEMENT_ID
where
ce.ENROLLMENTTYPE = 'Course'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
UNION
select c.id from course c
inner join COURSE_COURSEGROUP cg on cg.course_id = c.id
inner join COURSEGROUP_CUSTOMERENTITLEMENT cgce on cgce.COURSEGROUP_ID = cg.COURSEGROUP_ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cgce.CUSTOMERENTITLEMENT_ID
where
ce.ENROLLMENTTYPE = 'CourseGroup'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
)
group by [Name], [Description], [GUID], Bussinesskey
order by name, id asc
Here how I did this. I don't know how efficient this is but it's giving me what I am exactly wanting
select ID, [Name], [Description], [GUID], Bussinesskey from (
select row_number() over (partition by [Name] order by id desc) as row, t1.* from (
select
ID,
replace(replace(replace(replace([Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace([Description],',',''),'"','') [Description],
[GUID],
Bussinesskey
from course
where COURSESTATUS = 'Published' and RETIREDTF = 0
and bussinesskey in (
'PSTOAS0314001',
...
'RECEAL0510019'
)
and id in (
select c.id from course c
inner join COURSE_CUSTOMERENTITLEMENT cce on cce.COURSE_ID = c.ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cce.CUSTOMERENTITLEMENT_ID
where ce.ENROLLMENTTYPE = 'Course'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
UNION
select c.id from course c
inner join COURSE_COURSEGROUP cg on cg.course_id = c.id
inner join COURSEGROUP_CUSTOMERENTITLEMENT cgce on cgce.COURSEGROUP_ID = cg.COURSEGROUP_ID
inner join CUSTOMERENTITLEMENT ce on ce.id = cgce.CUSTOMERENTITLEMENT_ID
where ce.ENROLLMENTTYPE = 'CourseGroup'
and ce.customer_id = 23753
and c.COURSESTATUS = 'Published' and c.RETIREDTF = 0
)
)t1
) t
where t.row=1
order by t.name, t.id asc
Thanks
;with Course as
(
Select 50000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB1' BusinessKey
UNION
Select 60000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB2' BusinessKey
UNION
Select 70000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB3' BusinessKey
UNION
Select 80000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB4' BusinessKey
UNION
Select 90000 ID, 'Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB5' BusinessKey
UNION
Select 40000 ID, 'NOT Contracts' Name, '<BLURB>' [Description], '<GUID>' GUID, 'ARB1' BusinessKey
),
Course_Group AS
(
Select 50000 Course_ID, 1 COURSEGROUP_ID
UNION
Select 60000 Course_ID, 1 COURSEGROUP_ID
UNION
Select 70000 Course_ID, 1 COURSEGROUP_ID
UNION
Select 80000 Course_ID, 1 COURSEGROUP_ID
UNION
Select 90000 Course_ID, 2 COURSEGROUP_ID
UNION
Select 40000 Course_ID, 1 COURSEGROUP_ID
),
CourseGroup_CustomerEntitlement AS
(
Select 1 COURSEGROUP_ID, 1 CUSTOMERENTITLEMENT_ID
--UNION
--Select 2 COURSEGROUP_ID, 2 CUSTOMERENTITLEMENT_ID
--UNION
--Select 2 COURSEGROUP_ID, 4 CUSTOMERENTITLEMENT_ID
),
Course_CustomerEntitlement AS
(
Select 50000 Course_ID, 3 CUSTOMERENTITLEMENT_ID
UNION
Select 60000 Course_ID, 3 CUSTOMERENTITLEMENT_ID
UNION
Select 90000 Course_ID, 3 CUSTOMERENTITLEMENT_ID
UNION
Select 40000 Course_ID, 4 CUSTOMERENTITLEMENT_ID
),
CustomerEntitlement AS
(
Select 1 ID, 23753 Customer_ID, 'CourseGroup' ENROLLMENTTYPE
UNION
Select 2 ID, 7 Customer_ID, 'NOT COURSE GROUP' ENROLLMENTTYPE
UNION
Select 3 ID, 23753 Customer_ID, 'Course' ENROLLMENTTYPE
UNION
Select 4 ID, 7 Customer_ID, 'NOT COURSE' ENROLLMENTTYPE
),
CTE_base_data as (
SELECT
C.ID,
replace(replace(replace(replace(C.[Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace(C.[Description],',',''),'"','') [Description],
C.[GUID],
C.Businesskey
FROM Course C
JOIN Course_Group CG ON CG.Course_ID = C.ID
JOIN CourseGroup_CustomerEntitlement CG_CE ON CG_CE.COURSEGROUP_ID = CG.COURSEGROUP_ID
JOIN CustomerEntitlement CE_GROUP ON CE_GROUP.ID = CG_CE.CUSTOMERENTITLEMENT_ID
AND CE_GROUP.ENROLLMENTTYPE = 'CourseGroup'
WHERE
CE_GROUP.Customer_ID = 23753
UNION
SELECT
C.ID,
replace(replace(replace(replace([Name],',',''),'"',''),':',''),'?','') [Name] ,
replace(replace([Description],',',''),'"','') [Description],
[GUID],
Businesskey
FROM Course c
JOIN Course_CustomerEntitlement CCE ON CCE.Course_ID = C.ID
JOIN CustomerEntitlement CE_COURSE ON CE_COURSE.ID = CCE.CUSTOMERENTITLEMENT_ID
AND CE_COURSE.ENROLLMENTTYPE = 'COURSE'
WHERE
CE_COURSE.Customer_ID = 23753
--and COURSESTATUS = 'Published'
--and RETIREDTF = 0
--and businesskey in
-- (
-- ...
-- )
),
CTE_max_data as (
Select name, max(ID) ID
from CTE_base_data
group by name
)
Select Data.*
from CTE_base_data Data
JOIN CTE_max_data Filter
ON Data.ID = Filter.ID
AND Data.Name = Filter.Name
order by name, id asc
This yields:
ID Name Description GUID Businesskey
90000 Contracts ARB5
40000 NOT Contracts ARB1
Please advise where my data assumptions fall flat.

NOCYCLE in Postgres

I have a Oracle query with a NOCYCLE clause which I have to translate to Postgres:
SELECT FG_ID,CONNECT_BY_ROOT FG_ID as Parent_ID
FROM FG t
START WITH t.Parent_filter_group_id is null
CONNECT BY NOCYCLE PRIOR t.FILTER_GROUP_ID = t.PARENT_FILTER_GROUP_ID
I have converted this one with the help of the question and answer in
connect_by_root equivalent in postgres
as
with recursive fg_tree as (
select FG_ID,
FG_ID as fg
from FG
where Parent_filter_group_id is null
union all
select c.FG_ID,
p.fg
from FG c join fg_tree p on p.FG_ID = PARENT_FILTER_GROUP_ID
)
select * from fg_tree
order by FG_ID
but in this there is no clause for NOCYCLE if the parent is also one of the children then this query will return error.
You can collect the IDs for each level and then join on the condition that the "current" id is not contained in the path:
with recursive fg_tree as (
select FG_ID,
FG_ID as fg,
array[fg_id] as path
from FG
where Parent_filter_group_id is null
union all
select c.FG_ID,
p.fg,
p.fg||c.fg_id
from FG c
join fg_tree p on p.FG_ID and c.fg_id <> ALL (p.path)
)
select fg_id, fg
from fg_tree
order by filter_group_id
oracle version:
SELECT o.object_id, p.plugin_id AS plugin_id, LEVEL, CONNECT_BY_ISCYCLE "Cycle"
FROM sst_cycle_obj o LEFT JOIN sst_cycle_devplug p ON p.device_id = o.object_id
WHERE CONNECT_BY_ISCYCLE = 1
CONNECT BY NOCYCLE o.object_id = PRIOR p.plugin_id
START WITH o.object_id = 11
create table sst_cycle_obj
(object_id numeric(10))
create table sst_cycle_devplug
(device_id numeric(10)
,plugin_id numeric(10))
insert into sst_cycle_obj (object_id)
(select 11 from dual
union all
select 12 from dual
union all
select 13 from dual)
insert into sst_cycle_devplug (device_id,plugin_id)
(select 11, 12 from dual
union all
select 12, 13 from dual
union all
select 13,11 from dual)
-->NOCYCLE2 CONNECT_BY_ISCYCLE "Cycle" Postgresql version
WITH recursive ncot
AS
(SELECT a.device_id,a.plugin_id,('{'||a.device_id||'}')::numeric[] as PATH, 0 AS cycle1, 1::int as level
FROM ( select o.object_id device_id, p.plugin_id as plugin_id
from sst_cycle_obj o
left join sst_cycle_devplug p on p.device_id=o.object_id
) a
WHERE a.device_id = 11
UNION ALL
SELECT objt.device_id,objt.plugin_id, ncot.path||objt.device_id::numeric as PATH, CASE WHEN objt.plugin_id = any(ncot.path) THEN 1 else 0 END AS cycle1, ncot.level + 1 as level
FROM ( select o.object_id device_id, p.plugin_id as plugin_id
from sst_cycle_obj o
left join sst_cycle_devplug p on p.device_id=o.object_id
) objt
JOIN ncot ON objt.device_id = ncot.plugin_id and objt.device_id <> ALL (ncot.path)
)
SELECT device_id,plugin_id,PATH,cycle1,level
FROM ncot
WHERE cycle1=1
--<

SQL - UNION, priority on the first select statement when doing order by

I'm trying to print out the results from the "GermanDB" Database first, while also showing everything from the Boston DB that was not in the German database. Can this be done in one query?
My query (the bold part functions but does not order the way I want)
select * from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select * from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by ta.ProductRef** , tb.productRef
Just add one field to signal the priority. Like this:
select *, 0 as Priority from (
SELECT DISTINCT a.ProductRef
FROM GERMANDB.dbo.LOCATIONS AS a INNER JOIN GERMANDB.dbo.ITEMS AS b ON a.ProductRef = b.ProductRef
WHERE b.ACTIVE=1
) ta
UNION select *, 1 as Priority from
SELECT DISTINCT c.ProductRef
FROM BOSTONDB.dbo.LOCATIONS AS c INNER JOIN BOSTONDB.dbo.ITEMS AS d ON c.ProductRef = d.ProductRef
WHERE c.ACTIVE=1 (c.ProductRef NOT IN
(SELECT ProductRef FROM GERMANDB.dbo.ITEMS where ACTIVE=1))
) tb
order by Priority, ProductRef

Need help on SQL join

I have 2 tables as below:
select 1 as id,
'A' as place
into #places
UNION
select 2 as id,
'B' as place
UNION
select 3 as id,
'C' as place
UNION
select 4 as id,
'D' as place
select 'x' as name,
2 as start,
3 as endd
into #travel
UNION
select 'y' as name,
4 as start,
1 as endd
UNION
select 'z' as name,
1 as start,
3 as endd
select * from #places
select * from #travel
------------------------------
Now I want to get name/start place/end place using joins?
SELECT
t.name,
p1.place as start_place,
p2.place as end_place
FROM
travel t
JOIN places p1 on t.start = p1.id
JOIN places p2 on t.endd = p2.id
select #travel.*, pstart.place as start, pend.place as end
from #travel inner join #places as pstart on pstart.id = #travel.start
inner join #places as pend on pend.id = #travel.end