Pull data from a Cross Join - db2

I am trying to pull a series to columns from 2 tables. One of these columns is the most recent date comprised of 2 integers, a month and a year.
When I embed the select into the normal query (Max(timestamp_format....)) it returns the first date in the database even with the Max function. I have tried creating a subquery (Select ... select(timestamp_format... from..) ) and it gives me errors.
I am now trying to create it using a cross join fetching 1 row only and it is still giving me errors.
Here is the relevant part of my query including the where clause and the cross join.
T2.Enddate
From SCDATA.SCCLNT
Inner Join SCDATA.SCPLHS On SCDATA.SCPLHS.HCLNT = SCDATA.SCCLNT.CCLNT
Cross Join(
Select(
TIMESTAMP_FORMAT(SCDATA.SCPLHS.HYEAR || '-' || SCDATA.SCPLHS.HMONTH || '-01', 'yy-MM-dd') as EndDate
From SCDATA.SCPLHS
Order By EndDate desc fetch first 1 rows
)As T2
)
but it is still giving me the same error - after the Timestamp_format() it doesn't recognize any other code - neither as nor from are accepted.
How can I create this query correctly?
Thanks

Related

how to concatenate timestamp in different rows in postgresql?

I'm looking for a way to concatenate timestamp in two difference row, for an example, I have this table:
I want it to be grouped by weekday and concatenate the min(start_hour) with max(start_hour), to get something like this
and I'm using this query to retrieve the first image result
The query below should give you what you are looking for provided the information supplied. I made some assumptions. That the '00:00:00' in the start and end hours is not a valid time and can be ignored. If they should be considered valid, then Friday's output would be one entry of '00:00:00' - '11:30:00'.
I created two CTEs, one for the start hours and the other for the end hours where the values are not '00:00:00'. Added a row number to the CTEs so i can match up the day & row_number to get you a set.
SELECT day
,array_to_string(array_agg(t.shift), ',') shifts
FROM (
WITH cte_start AS (
SELECT row_number() OVER (PARTITION BY day)
,day
,start_hour
FROM test22
WHERE start_hour <> '00:00:00'::time
)
,cte_stop AS (
SELECT row_number() OVER (PARTITION BY day)
,day
,stop_hour
FROM test22
WHERE stop_hour <> '00:00:00'::time
)
SELECT cte_start.day
,cte_start.start_hour::varchar || ' - ' || cte_stop.stop_hour::varchar AS shift
FROM cte_start
LEFT OUTER JOIN cte_stop ON cte_start.day = cte_stop.day
AND cte_start.row_number = cte_stop.row_number
) T
GROUP BY T.day
-HTH

PostgreSQL crosstab() alternative with CASE and aggregates

I want to create a pivot table view showing month on month sum of bookings for every travel_mode.
Table bookings:
timestamp
, bookings
, provider_id
Table providers:
provider_id
, travel_mode
Pivot table function and crosstab functions are not to be used to do this. So I am trying to use JOIN and CASE. Following is the query:
SELECT b.month,
(CASE WHEN p.travel_mode=train then b.amount end)train,
(CASE WHEN p.travel_mode=bus then b.amount end)bus,
(CASE WHEN p.travel_mode=air then b.amount end)air
FROM
(SELECT to_char(date_,month) as month, travel_mode, sum(bookings) as amount
from bookings as b
join providers as p
on b.provider_id=p.provider_id
group by b.month, p.travel_mode)
group by b.month;
However I am getting an error which says:
subquery in FROM must have an alias LINE 6:
And when I add an alias it throws an error saying:
column p.travel_mode must appear in the GROUP BY clause or be used in an aggregate function
LINE 2:
The final result should be something like this
Month Air Bus Train
01 Amount(air) Amount(Bus) Amount(train)
I have a feeling it is a minor error somewhere but I am unable to figure it out at all.
P.S. I had to remove all quotations in the question as it was not allowing me to post this. But those are being taken care of in the actual query.
Multiple problems. The missing table alias is just one of them. This query should work:
SELECT month
, sum(CASE WHEN travel_mode = 'train' THEN amount END) AS train
, sum(CASE WHEN travel_mode = 'bus' THEN amount END) AS bus
, sum(CASE WHEN travel_mode = 'air' THEN amount END) AS air
FROM (
SELECT to_char(timestamp, 'MM') AS month, travel_mode, sum(bookings) AS amount
FROM bookings b
JOIN providers p USING (provider_id)
GROUP BY month, p.travel_mode
) sub
GROUP BY month;
Missing single quotes for string literals. (You seem to have removed those being under the wrong impression you couldn't post quotations.)
Missing table alias for the subquery - just like the 1st error message says.
In the outer query, table names (or aliases) of underlying tables in the subquery are not visible. Only the table alias of the subquery is. Since there is only one subquery, you don't need table-qualification at all there.
month is an output column name (not in the underlying table), so the table qualification b.month was wrong, too.
You seem to want 2-digit numbers for months. Use the template pattern 'MM' instead of 'month' with to_char().
The aggregation in the outer query does not work like you had it - just like your 2nd error message says. You have to wrap the outer CASE expression in a aggregate function. You might as well use min() or max() in this case, because there are never more than one rows after the subquery.
Still unclear where date_ is coming from? You mean timestamp? (which is not a good identifier).
But you don't need the subquery to begin with and can simplify to:
SELECT to_char(timestamp, 'MM') AS month
, sum(CASE WHEN p.travel_mode = 'train' THEN b.bookings END) AS train
, sum(CASE WHEN p.travel_mode = 'bus' THEN b.bookings END) AS bus
, sum(CASE WHEN p.travel_mode = 'air' THEN b.bookings END) AS air
FROM bookings b
JOIN providers p USING (provider_id)
GROUP BY 1;
For best performance you should still use crosstab(), though:
PostgreSQL Crosstab Query
You have to name the subquery as the error message says:
SELECT b.month,
(CASE WHEN p.travel_mode=train then b.amount end)train,
(CASE WHEN p.travel_mode=bus then b.amount end)bus,
(CASE WHEN p.travel_mode=air then b.amount end)air
FROM
(SELECT to_char(date_,month) as month, travel_mode, sum(bookings) as amount
from bookings as b
join providers as p
on b.provider_id=p.provider_id
group by b.month, p.travel_mode)
**as foo** group by b.month;
Remove the stars to make it work.

Finding Detail Based on date in SSRS

I have a Table that I am using to pull order details in SSRS that has when the price of a product number was changed. It has Data Changed and Updated Cost.
I am pairing up two different tables to create a report that is the cost of the package at the time of the order. Here is how I am pulling my data:
SELECT
WAREHOUSE.ActPkgCostHist.ItemNo AS [ActPkgCostHist ItemNo]
,WAREHOUSE.ActPkgCostHist.ActPkgCostDate
,WAREHOUSE.ActPkgCostHist.ActPkgCost
,ORDER.OrderHist.OrderNo
,ORDER.OrderHist.ItemNo AS [OrderHist ItemNo]
,ORDER.OrderHist.DispenseDt
FROM
WAREHOUSE.ActPkgCostHist
INNER JOIN ORDER.OrderHist
ON WAREHOUSE.ActPkgCostHist.ItemNo = ORDER.OrderHist.ItemNo
Catalog=ShippedOrders
ActPkgCostHist Table has What the cost of an Item was and what date the cost was changed.
OrderHist Table has the complete details of the order except the ActPkgCost at the time of the purchase.
I am attempting to create a table that Has order number, the date of the order and the package cost at the time of the order.
The ROW_NUMBER function is very useful for cases like this.
SELECT WAREHOUSE.ActPkgCostHist.ItemNo AS [ActPkgCostHist ItemNo]
,WAREHOUSE.ActPkgCostHist.ActPkgCostDate
,WAREHOUSE.ActPkgCostHist.ActPkgCost
,ORDER.OrderHist.OrderNo
,ORDER.OrderHist.ItemNo AS [OrderHist ItemNo]
,ORDER.OrderHist.DispenseDt
FROM ORDER.OrderHist
INNER JOIN (
SELECT ItemNo, ActPkgCostDate, ActPkgCost
, ROW_NUMBER() OVER (PARTITION BY ItemNo ORDER BY ActPkgCostDate DESC) as RN
FROM WAREHOUSE.ActPkgCostHist
--if there are future dated changes, limit ActPkgCostDate to be <= the current date
) ActPkgCostHist on ActPkgCostHist.ItemNo = OrderHist.ItemNo
WHERE RN = 1
What this subquery does is group the cost history by ItemNo. Then for each one, it ranks the changes by recency with the most recent change being 1. Then in the main query you filter it to just rows with a 1.
For each item in each order you have to find the latest cost date and use it when joining with the cost table
SELECT C.ItemNo AS [ActPkgCostHist ItemNo],
C.ActPkgCostDate,
C.ActPkgCost,
O.OrderNo,
O.ItemNo AS [OrderHist ItemNo],
O.DispenseDt
FROM WAREHOUSE.ActPkgCostHist AS C
-- JOIN order detail with cost table in order to define the cost date per item/order
INNER JOIN (SELECT Max(CH.ActPkgCostDate) AS ItemCostDate,
OH.OrderNo,
OH.ItemNo,
OH.DispenseDt
FROM WAREHOUSE.ActPkgCostHist AS CH
INNER JOIN ORDER.OrderHist AS OH
ON CH.ItemNo = OH.ItemNo
-- Get the latest cost date only from dates before order date
WHERE CH.ActPkgCostDate <= OH.DispenseDt
GROUP BY OH.OrderNo,
OH.ItemNo,
OH.DispenseDt) AS O
ON C.ItemNo = O.ItemNo
AND C.ActPkgCostDate = O.ItemCostDate

Creating 'Empty' Records for Days of the Month Without Records

I have a very simpl postgres (9.3) query that looks like this:
SELECT a.date, b.status
FROM sis.table_a a
JOIN sis.table_b b ON a.thing_id = b.thing_id
WHERE EXTRACT(MONTH FROM a.date) = 06
AND EXTRACT(YEAR FROM a.date) = 2015
Some days of the month of June do not exist in table_a and thus are obviously not joined to table_b. What is the best way to create records for these not represented days and assign a placeholder (e.g. 'EMPTY') to their 'status' column? Is this even possible to do using pure SQL?
Basically, you need LEFT JOIN and it looks like you also need generate_series() to provide the full set of days:
SELECT d.date
, a.date IS NOT NULL AS a_exists
, COALESCE(b.status, 'status_missing') AS status
FROM (
SELECT date::date
FROM generate_series('2015-06-01'::date
, '2015-06-30'::date
, interval '1 day') date
) d
LEFT JOIN sis.table_a a USING (date)
LEFT JOIN sis.table_b b USING (thing_id)
ORDER BY 1;
Use sargable WHERE conditions. What you had cannot use a plain index on date and has to default to a much more expensive sequential scan. (There are no more WHERE conditions in my final query.)
Aside: don't use the basic type name (and reserved word in standard SQL) date as identifier.
Related (2nd chapter):
PostgreSQL: running count of rows for a query 'by minute'

Aggregate function with Date on Postgres

I'm kind of rusty on my SQL, maybe you can help me out on this query.
I have these two tables for a tickets system (I'm omitting some fields):
table tickets
id - bigint
subject - text
user_id - bigint
closed - boolean
first_message - bigint
(foreign key, for next table's id)
last_message - bigint
(same as before)
table ticket_messages
creation_date
I need to query the closed tickets, and make an average of the time spent between the first message creation_date and the last message creation_date. This is what I've done so far:
SELECT t.id, t.subject, tm.creation_date
FROM tickets AS t
INNER JOIN ticket_messages AS tm
ON tm.id = t.first_message
OR tm.id = t.last_message
WHERE t.closed = true
I'm looking for some group by or aggregate function to get all the data from the table, and try to calculate the time spent between last and first, also trying to display the dates for the first and last message.
UPDATE I added an inner Join with the second table instead of "OR", now I get both dates, and I can find the sum from my application:
SELECT t.id, t.subject, tm.creation_date, tm2.creation_date
FROM tickets AS t
INNER JOIN ticket_messages AS tm
ON tm.id = t.first_message
INNER JOIN ticket_messages as tm2
ON tm2.id = t.last_message
WHERE t.closed = true
I think that did it...
Something like this should do for getting the nr of days elapsed. You might need to put this in a subquery to easily pull out more fields from 'tickets'.
SELECT t.id,AVG(tlast.creation_date - tfirst.creation_date)
FROM tickets AS t
INNER JOIN ticket_messages AS tfirst
ON tm.id = t.first_message
INNER JOIN ticket_messages AS tlast
ON tm.id = t.last_message
WHERE t.closed = true
GROUP BY t.id
Which might lead to(not tested..) e.g.
select t.id,t.subject,sub.nr_days
FROM (
SELECT t.id,AVG(tlast.creation_date - tfirst.creation_date) as nr_days
FROM tickets AS t
INNER JOIN ticket_messages AS tfirst
ON tm.id = t.first_message
INNER JOIN ticket_messages AS tlast
ON tm.id = t.last_message
WHERE t.closed = true
GROUP BY t.id ) AS sub
INNER JOIN tickets AS t
ON sub.id = t.id;
You are trying to combine two queries into one and trying to get the data from three rows of data from two tables. Both need to be fixed.
First of all, you should not attempt to mix aggregate data (such as averages) with the details for single items - you need separate queries for that. You can do it, but the output is repetitious and therefore wasteful (all the single items in a group will have the same aggregate data).
Secondly, you need to find the first message and the last message for a given ticket. Hence, that query is:
SELECT t.id, t.subject, tm1.creation_date as start, tm2.creation_date as end,
tm2.creation_date - tm1.creation_date as close_interval
FROM tickets AS t
INNER JOIN ticket_messages AS tm1 ON t.last_message = tm1.id
INNER JOIN ticket_messages AS tm2 ON t.last_message = tm2.id
WHERE t.closed = true
This gives you three rows of data per result row - as required. The computed value should be an interval type - assuming that PostgreSQL actually has that type. (In Informix, the type would effectively be INTERVAL DAY(n) for a suitable n, such as 9.)
You can average those intervals, now. You can't average dates because dates cannot be added together and cannot be divided; averaging involves both summing and dividing. Intervals can be added and divided.