Query result not working with array type in PostgreSQL - sql

I have two tables which contains a column with data type array in PostgreSQL. The structure is like below:
tbl_tour_packages
tbl_header_images
I have a query which contains several joins. The query is working fine with other joins and showing no error. But missing the values from tbl_header_images.
The query is:
SELECT
t1.tour_id AS pid,
t1.tour_name AS title,
t1.tour_duration AS nights,
t1.tour_price_full AS price,
t1.discount AS discount,
t1.tour_seo_title AS seo,
t3.category AS category,
t4.image_names[1] AS image_url,
CASE WHEN max(s.state_name) IS NULL THEN NULL ELSE array_agg(s.state_name) END AS state,
CASE WHEN max(o.destination) IS NULL THEN NULL ELSE array_agg(o.destination) END AS destinations
FROM tbl_tour_packages t1
LEFT JOIN tbl_countries t2 ON t1.tour_country_iso = t2.iso
LEFT JOIN tbl_categories t3 on t1.tour_category_id = t3.id
LEFT JOIN tbl_header_images t4 ON t1.tour_id = t4.package_id
LEFT JOIN tbl_states AS s ON (t1.tour_state #> array[s.state_code])
LEFT JOIN tbl_destinations AS o ON (t1.tour_destination #> array[o.id])
WHERE t1.tour_status = 1
GROUP BY 1,7,8
ORDER BY view_count ASC LIMIT 6
I want to get the 'image_name' from tbl_header_images. Any quick help or suggestion will be appreciated.

before WHERE clause you should be able to do something like:
, unnest(image_names) _image_names
and then in select statement aggregate that back into an array
array_agg(_image_names) AS image_names
I don't quite get the t4.image_names[1] AS image_url attempt, but I'm sure you can pick it up from here.
so the whole query would be something like:
edit: I've stripped extra groupping
SELECT
t1.tour_id AS pid,
t1.tour_name AS title,
t1.tour_duration AS nights,
t1.tour_price_full AS price,
t1.discount AS discount,
t1.tour_seo_title AS seo,
t3.category AS category,
(array_agg(_image_names))[1] AS image_url,
CASE WHEN max(s.state_name) IS NULL THEN NULL ELSE array_agg(s.state_name) END AS state,
CASE WHEN max(o.destination) IS NULL THEN NULL ELSE array_agg(o.destination) END AS destinations
FROM tbl_tour_packages t1
LEFT JOIN tbl_countries t2 ON t1.tour_country_iso = t2.iso
LEFT JOIN tbl_categories t3 on t1.tour_category_id = t3.id
LEFT JOIN tbl_header_images t4 ON t1.tour_id = t4.package_id
LEFT JOIN tbl_states AS s ON (t1.tour_state #> array[s.state_code])
LEFT JOIN tbl_destinations AS o ON (t1.tour_destination #> array[o.id])
, unnest(t4.image_names) AS _image_names
WHERE t1.tour_status = 1
GROUP BY 1,7
ORDER BY view_count ASC LIMIT 6
alternatively I'd go with subselect:
SELECT t1.*,
(SELECT image_names[1] FROM tbl_header_images WHERE package_id = t1.tour_id) AS image_url
FROM t1, t2, t3
WHERE ...

Related

How to get rid of the redundant information of a query and be left with only one row that contains all the information

Hello I have the following query in postgres. And I get the following result.
create temporary table "query_table" as
select
"airgoLocator_surgerytimes".iqnum as "surgery",
adminssion_time as "Admissió in",
pre_enter_time as "Preparació in",
quiro_enter_time as "Quiròfan in",
quiro_exit_time as "Quiròfan out",
recu_enter_time as "Recuperació in",
exit_time as "Sortida",
max(case when ("airgoLocator_phase".name='Inici anestèsia') then "airgoLocator_phasehistory".timestamp else NULL end) as Inici_anestesia,
max(case when ("airgoLocator_phase".name='Inici cirurgia') then "airgoLocator_phasehistory".timestamp else NULL end) as Inici_cirurgia,
max(case when ("airgoLocator_phase".name='Fi cirurgia') then "airgoLocator_phasehistory".timestamp else NULL end) as Fi_cirurgia,
max(case when ("airgoLocator_phase".name='Fi anestèsia') then "airgoLocator_phasehistory".timestamp else NULL end) as Fi_anestesia
from
"airgoLocator_surgerytimes"
inner join
"airgoLocator_surgery"
on
"airgoLocator_surgerytimes".iqnum = "airgoLocator_surgery".iqnum
inner join
"airgoLocator_phasehistory"
on
"airgoLocator_surgery".id = "airgoLocator_phasehistory".surgery_id
inner join
"airgoLocator_phase"
on
"airgoLocator_phasehistory".phase_id = "airgoLocator_phase".id
--where "airgoLocator_surgerytimes".iqnum = '0018571064'
group by "airgoLocator_surgerytimes".iqnum, "airgoLocator_surgerytimes".adminssion_time,
"airgoLocator_surgerytimes".pre_enter_time, "airgoLocator_surgerytimes".quiro_enter_time,
"airgoLocator_surgerytimes".quiro_exit_time, "airgoLocator_surgerytimes".recu_enter_time,
"airgoLocator_surgerytimes".exit_time, "airgoLocator_phase".name, "airgoLocator_phasehistory".timestamp,
"airgoLocator_surgerytimes".id, "airgoLocator_phasehistory".phase_id
order by "airgoLocator_surgerytimes".id desc, phase_id asc
;
select * from "query_table" where surgery = '0018571064';
My question is how can I get rid of the null fields and the repeated data so that I am left with a single row with all the information. For example like this:
Due to the way you are adding the rows into your temp table, with null values, we have to filter for the columns that do not have null values. You can get the result you want in two different ways. Using either group by, or using inner join on the same table multiple times. Here is an example of both:
Using group by:
select distinct
t1.surgury,
t1.Admission,
t1.preperation,
t1.Quirofan_in,
t1.Quirofan_out,
t1.recuperacion,
t1.sortida,
max(t1.inici_anestesia) [inici_anestesia],
max(t1.inici_cirurgia)[inici_cirurgia],
max(t1.fi_cirurgia)[fi_cirurgia]
from table2 t1
group by surgury, Admission, preperation, Quirofan_in, Quirofan_out, recuperacion, sortida
Using inner join method:
select distinct
t1.surgury,
t1.Admission,
t1.preperation,
t1.Quirofan_in,
t1.Quirofan_out,
t1.recuperacion,
t1.sortida,
t2.inici_anestesia,
t3.inici_cirurgia,
t4.fi_cirurgia
from table2 t1
inner join table2 t2 on t2.surgury=t1.surgury and t2.inici_anestesia is not null
inner join table2 t3 on t3.surgury=t1.surgury and t3.inici_cirurgia is not null
inner join table2 t4 on t2.surgury=t1.surgury and t4.fi_cirurgia is not null
I can tell you a solution, but you need to know your data and check if this solution does not match with your expected result.
You can remove those columns from group by clause and wrap them with MAX Function in SELECT clause.
But if the values in different rows (except null values) are different you miss them. In this case you may decide to use SUM function. It depends on your business goal.
Because your query is very long; I provide you a simple sample to understand.
Create Table Test (
ID int,
groupColumn1 int,
groupColumn2 int
)
INSERT Test (ID, groupColumn1, groupColumn2)
Values (1, 10, 20),
(1, 10, NULL),
(1, NULL, 20)
select ID, Max(groupColumn1) groupColumn1, MAX(groupColumn2) groupColumn2
From Test
Group by ID

Oracle SQL Developer - error when referencing fields within nested from statements

For the below query I am getting an error with line 4 when referencing variables within "y". The query runs successfully when I use just " y.* " (line 5), however it generates an error when I try to also pull from the specified fields in line 4 (y.field1 as PRODUCT, y.field2 as PRODUCT_TYPE, y.entity, y.TYPE1). For the output, I want these fields listed first for visual reference.
I have this approach/ logic working for other queries (as i'm re using this logic for multiple variations of queries and various tables). However, I think that the issue with this one lies in my attempt to reference fields from tables that are in my join statements.
(
select
-- categorization fields:
-- table2.field1 as PRODUCT, table2.field2 as PRODUCT_TYPE, table3.entity, table3.TYPE1
y.field1 as PRODUCT,
y.field2 as PRODUCT_TYPE,
y.entity,
y.TYPE1
,y.*
from (
select *
from (
-- table references:
select table1.*,
row_number() over (
partition by
-- categorization fields:
table2.field1,
table2.field2,
table3.entity,
table3.TYPE1
order by table3.entity
) as rn
-- table references
from table1
-- joins, links, and filtering:
inner join table6 on table1.field_1 = table6.code1
inner join table5 on (table6.code = table5.code1)
AND (table6.code = table5.code)
left join table3 on table6.ent1 = table3.ent_code
left join table2 on table1.extid = table2.extID
where table1.tdate between '01-APR-19' and '01-APR-21'
AND table1.refe NOT IN ('OFF')
) x
-- sample rows:
where rn <= 2
) y
);
Let me know if anyone has a way that I can maybe better specify which tables those fields come from. I wish I could just do something like this:
y.table2.field1 as PRODUCT,
y.table2.field2 as PRODUCT_TYPE,
y.table3.entity,
y.table3.TYPE1
Sorry that I don't have a fiddle available!
Let me know if anyone has a way that I can maybe better specify which tables those fields come from.
Don't use select *. Instead, use the column names and give them appropriate aliases so you know where they came from:
As an example:
SELECT small_value,
medium_value,
big_value
FROM (
SELECT small.value AS small_value,
medium.value AS medium_value,
big.value AS big_value
FROM big
CROSS JOIN medium
CROSS JOIN small
)
WHERE 1 = 1
In your query, instead of using SELECT * in y or using SELECT table1.* in x you can name the columns and give them descriptive aliases.
I am getting an error with line 4 when referencing variables within "y".
(
select
-- categorization fields:
-- table2.field1 as PRODUCT, table2.field2 as PRODUCT_TYPE, table3.entity, table3.TYPE1
That is because you cannot see TABLE2 or TABLE3 because the only "view" you are looking at is of the sub-query with the alias y.
If you want to see those columns then you need to SELECT them inside the x subquery and pass them to each subsequent outer-query.
(
select *
from (
-- table references:
select table1.field1 AS t1_product,
table1.field2 AS t1_product_type,
table1.entity AS t1_entity,
table1.type1 AS t1_type1,
table2.field1 AS t2_product,
table2.field2 AS t2_product_type,
table2.entity AS t2_entity,
table2.type1 AS t2_type1,
table3.field1 AS t3_product,
table3.field2 AS t3_product_type,
table3.entity AS t3_entity,
table3.type1 AS t3_type1,
row_number() over (
partition by
-- categorization fields:
table2.field1,
table2.field2,
table3.entity,
table3.TYPE1
order by table3.entity
) as rn
-- table references
from table1
-- joins, links, and filtering:
inner join table6 on table1.field_1 = table6.code1
inner join table5 on (table6.code = table5.code1)
AND (table6.code = table5.code)
left join table3 on table6.ent1 = table3.ent_code
left join table2 on table1.extid = table2.extID
where table1.tdate between '01-APR-19' and '01-APR-21'
AND table1.refe NOT IN ('OFF')
) x
-- sample rows:
where rn <= 2
);

Why does this SQL SELECT only work when a value is specified?

Following SQL statement gives me a correct result when putting a specific load into the where clause [and a.t$load = '123456789'].
SELECT a.t$type, a.t$load, b.t$wght, k.calc FROM table1 a
LEFT JOIN table2 b
ON a.t$load = b.t$load and a.t$type = b.t$type
LEFT JOIN
(SELECT t$load, t$type, SUM(q.calculation) calc FROM
(SELECT y.t$type, y.t$load, (y.t$dqua * x.t$wght) calculation FROM table1 y
LEFT JOIN table3 x
ON x.t$item = y.t$mitm
) q
group by t$load, t$type) k
ON k.t$load = a.t$load and k.t$type = a.t$type
where a.t$type = '123'
and a.t$load = '123456789'
group by a.t$type, a.t$load, b.t$wght
When I remove this condition, every single calc value is NULL. Why is that? How can I connect the sub queries to calculate "calc" correctly by every load?
The tables are linked like this:
table1: the same load can appear multiple times with different items
table2: for every load there is a weight (-> relation from t1 to t2 is n:1)
table3: every item from t1 has a weight in t3 (1:1) which should be multiplied with the quantity and summed by load.
One null value can ruin the result for SUM and other SQL aggregation functions. trying adding an ISNULL the sum of calculation. I also added some validation in the nested queries.
SELECT
a.t$type, a.t$load, b.t$wght, k.calc
FROM
table1 a LEFT JOIN table2 b ON a.t$load = b.t$load and a.t$type = b.t$type
LEFT JOIN
(SELECT
t$load, t$type, SUM(ISNULL(q.calculation,0)) calc -- isnull check before we sum.
FROM
(SELECT y.t$type, y.t$load, (y.t$dqua * x.t$wght) calculation FROM table1 y
LEFT JOIN table3 x
ON x.t$item = y.t$mitm
where y.t$dqua is NOT NULL AND x.t$wght is NOT NULL
-- add these conditions, to eliminate nulls from the result.
) q
group by t$load, t$type) k
ON k.t$load = a.t$load and k.t$type = a.t$type
where
a.t$type = '123' and a.t$load = '123456789'
group by
a.t$type, a.t$load, b.t$wght
I hope this helps you down the right path.

Join same column from multiple tables

Below is my current code. I'm not sure what the best way is to amend this to give me the results I need.
SELECT
T1.SC,
T1.AN,
T1.DOFS_DATE,
T2.M_ID,
T3.OPDT,
T4.MARKER,
T5.E_DTE,
T5.E_TME,
T5.E_PST_DTE,
T5.E_AMT,
T5.E_NAR_O,
T5.E_NAR_T
FROM E_Base.AR_MyTable T1
LEFT JOIN E_Base.Translation T2
ON T1.SC = T2.SC
AND T1.AN = T2.AN
LEFT JOIN E_Base.BA T3
ON T2.M_ID = T3.M_ID
LEFT JOIN E_Base.APF T4
ON T3.M_ID = T4.M_ ID
AND MARKER = 54
LEFT JOIN U_DB.TEH_201804 T5
ON T2.M_ID = T5.M_ID
AND T1.DOFS_DATE = T5.E_PST_DTE
QUALIFY ROW_NUMBER() OVER (PARTITION BY T2.M_ID ORDER BY T2.ID_END_DATE DESC, T3.E_END_DATE DESC) = 1
The above code works. However, it is the final left join on T5 where I need help.
In T1 each M_ID has assigned it's own DOFS_DATE that could be any date within the year and I want the data from T5 U_DB.TEH_201804 for the matching date. However, 5 U_DB.TEH_201804 relates to only April 2018. There are 12 tables with the same database (201804, 201805, 201806 etc) that all have the exact same columns but relate to a different month within the year.
Ideally, I want to left join the columns from T5 once but search all 12 tables within the database to bring back the data where the dates correspond.
I was thinking UNION but am unsure how to work this in.
Any help would be greatly appreciated!
Thanks
You could change you code related to table t5 wuth a left join on a subquery that select the union all for all the bale you need ...... (i have named the subquery TT)
SELECT
T1.SC,
T1.AN,
T1.DOFS_DATE,
T2.M_ID,
T3.OPDT,
T4.MARKER,
TT.E_DTE,
TT.E_TME,
TT.E_PST_DTE,
TT.E_AMT,
TT.E_NAR_O,
TT.E_NAR_T
FROM E_Base.AR_MyTable T1
LEFT JOIN E_Base.Translation T2
ON T1.SC = T2.SC
AND T1.AN = T2.AN
LEFT JOIN E_Base.BA T3
ON T2.M_ID = T3.M_ID
LEFT JOIN E_Base.APF T4
ON T3.M_ID = T4.M_ ID
AND MARKER = 54
LEFT JOIN (
select *
FROM U_DB.TEH_201804
UNION ALL
select *
FROM U_DB.TEH_201805
UNION ALL
select *
FROM U_DB.TEH_201806
UNION ALL
select *
FROM U_DB.TEH_201807
UNION ALL
.....
) TT ON T2.M_ID = TT.M_ID
AND T1.DOFS_DATE = TT.E_PST_DTE
QUALIFY ROW_NUMBER() OVER (PARTITION BY T2.M_ID ORDER BY T2.ID_END_DATE DESC, T3.E_END_DATE DESC) = 1
It's hard to tell without additional details like explain and QueryLog step data.
Based on #scaisEdge answer:
You can try to move the first two joins into a Derived Table to apply the ROW_NUMBER early (possible because you do Outer Joins only):
SELECT
dt.*,
T4.MARKER,
TT.E_DTE,
TT.E_TME,
TT.E_PST_DTE,
TT.E_AMT,
TT.E_NAR_O,
TT.E_NAR_T
FROM
(
SELECT
T1.SC,
T1.AN,
T1.DOFS_DATE,
T2.M_ID,
T3.OPDT
FROM E_Base.AR_MyTable T1
LEFT JOIN E_Base.Translation T2
ON T1.SC = T2.SC
AND T1.AN = T2.AN
LEFT JOIN E_Base.BA T3
ON T2.M_ID = T3.M_ID
QUALIFY Row_Number()
Over (PARTITION BY T2.M_ID
ORDER BY T2.ID_END_DATE DESC, T3.E_END_DATE DESC) = 1
) AS dt
LEFT JOIN E_Base.APF T4
ON dt.M_ID = T4.M_ID
AND MARKER = 54
LEFT JOIN
(
SELECT *
FROM U_DB.TEH_201804
UNION ALL
SELECT *
FROM U_DB.TEH_201805
UNION ALL
SELECT *
FROM U_DB.TEH_201806
UNION ALL
SELECT *
FROM U_DB.TEH_201807
UNION ALL
.....
) TT
ON dt.M_ID = TT.M_ID
AND dt.DOFS_DATE = TT.E_PST_DTE
It might also help the optimizer to provide additional info about the data ranges. Those tables should have CHECK-constraints to tell the optimizer that they contain only data from a single month, if they don't exist try adding a WHERE-condition to each Select, e.g. WHERE E_PST_DTE BETWEEN DATE '2018-04-01' AND DATE '2018-04-30'.
Of course, always check Explain if the plan actually changes...

(probably) very simple SQL query needed

Having a slow day....could use some assistance writing a simple ANSI SQL query.
I have a list of individuals within families (first and last names), and a second table which lists a subset of those individuals. I would like to create a third table which flags every individual within a family if ANY of the individuals are not listed in the second table. The goal is essentially to flag "incomplete" families.
Below is an example of the two input tables, and the desired third table.
As I said...very simple...having a slow day. Thanks!
I think you want a left join and case expression:
select t1.*,
(case when t2.first_name is null then 'INCOMPLETE' else 'OK' end) as flag
from table1 t1 left join
table2 t2
on t1.first_name = t2.first_name and t1.last_name = t2.last_name;
Of course, this marks "Diane Thomson" as "OK", but I think that is an error in the question.
EDIT:
Oh, I see. The last name defines the family (that seems like a pretty big assumption). But you can do this with window functions:
select t1.*,
(case when count(t2.first_name) over (partition by t1.last_name) =
count(*) over (partition by t1.last_name)
then 'OK'
else 'INCOMPLETE'
end) as flag
from table1 t1 left join
table2 t2
on t1.first_name = t2.first_name and t1.last_name = t2.last_name;
That's not simple, at least not in SAS :-)
Standard SQL, when Windowed Aggregates are supported:
select ft.*,
-- counts differ when st.first_name is null due to the outer join
case when count(*) over (partition by ft.last_name)
= count(st.first_name) over (partition by ft.last_name)
then 'OK'
else 'INCOMPLETE'
end
from first_table as ft
left join second_table as st
on ft.first_name = st.first_name
and ft.last_name = ft.last_name
Otherwise you need to a standard aggregate and join back:
select ft.*, st.flag
from first_table as ft
join
(
select ft.last_name,
case when count(*)
= count(st.first_name)
then 'OK'
else 'INCOMPLETE'
end as flag
from first_table as ft
left join second_table as st
on ft.first_name = st.first_name
and ft.last_name = st.last_name
group by ft.last_name
) as st
on ft.last_name = st.last_name
It is pretty easy to do in SAS if you want to take advantage of its non-ANSI SQL feature of automatically re-merging aggregate function results back onto detail records.
select
a.first
, a.last
, case when 1=max(missing(b.last)) then 'INCOMPLETE'
else 'OK'
end as flag
from table1 a left join table2 b
on a.last=b.last and a.first=b.first
group by 2
order by 2,1
;