querying over multiple data from function in PostgreSQL - sql

I have a function which gives stock levels:
select * from stocklevel(ID)
This function gives stock leverl per ID.
For example:
select * from stocklevel(23)
Result is:
ID datelevel stocklevel
23 01.01.17 15
23 02.01.17 18
23 05.01.17 20
This is the stock level for ID=23.
Not all dates appear, it depends on many things.
I want to use this function to get the stock levels for all parts from my system
basically something like this:
select *,(select stocklevel from stocklevel(parts.partid) where datelevel = ??? )
from parts
OR:
select *
from parts
left join ( select stocklevel from stocklevel(parts.partid) where datelevel = ??? ) using (ID)
Now, the issue is with the WHERE condition I want to specific a specific date like '04.01.17' but if the date does not exist i want it to take the date before that so:
for the ID=23 and date '04.01.17' it should show 18,'02.01.17' .
for the ID=23 and date '15.01.13' it should show nothing.
for the ID=23 and date '05.01.17' it should show 20,'05.01.17' .
How can I do that?

First, I would use a lateral join:
select *
from parts p left join lateral
stocklevel(p.partid);
Then the issue is that you want the most recent level before or on a given date. You can do this using distinct on:
select distinct on (p.id) . . .
from parts p left join lateral
stocklevel(p.partid)
where datelevel <= ?
order by p.id, datelevel desc;
Note: This will not return parts that have no dates before the given date. You might want:
where datelevel is null or datelevel <= ?

select *
from parts
left join ( select stocklevel
from stocklevel(parts.partid)
where datelevel = (select max(datelevel)
from stocklevel(parts.partid) sl2
where sl2.datelevel <= '04.01.17') using(ID) ) using (ID)
untested...

Related

Bigquery replacing empty results or null values with some 000

I see for few dates data is not there, And now for the dates which data doesn't exist, i would like to replace it with zero instead of no results found. I tried as below and got the present output
select trvl_details.strt_dte as cre_dte,
trvl_typ_cde,
coalesce(count(1),0) as createdcount
from project.dataset.tableid JOIN UNNEST(trvl_details)trvl_details
WHERE trvl_details.strt_dte >= "2020-12-24" and trvl_typ_cde='AIR' group by 1,2
Can someone please help me with this?
You can use GENERATE_DATE_ARRAY to create a list of dates and then left join generated list of dates with your results:
WITH your_data AS (
select trvl_details.strt_dte as cre_dte, trvl_typ_cde, coalesce(count(1),0) as createdcount
from project.dataset.tableid JOIN UNNEST(trvl_details)trvl_details
WHERE trvl_details.strt_dte >= "2020-12-24" and trvl_typ_cde='AIR'
group by 1,2
)
SELECT day, your_data.trvl_typ_cde, IFNULL(your_data.createdcount, 0)
FROM UNNEST(GENERATE_DATE_ARRAY('2020-12-01', '2020-12-31')) as day
LEFT JOIN your_data
ON day = your_data.cre_dte

Calculate MAX for every row in SQL

I have this tables:
Docenza(id, id_facolta, ..., orelez)
Facolta(id, ...)
and I want to obtain, for every facolta, only the id of Docenza who has done the maximum number of orelez and the number of orelez:
id_docenzaP facolta1 max(orelez)
id_docenzaQ facolta2 max(orelez)
...
id_docenzaZ facoltaN max(orelez)
how can I do this? This is what i do:
SELECT DISTINCT ... F.nome, SUM(orelez) AS oreTotali
FROM Docenza D
JOIN Facolta F ON F.id = D.id_facolta
GROUP BY F.nome
I obtain somethings like:
docenzaP facolta1 maxValueForidP
docenzaQ facolta1 maxValueForidQ
...
docenzaR facolta2 maxValueForidR
docenzaS facolta2 maxValueForidS
...
docenzaZ facoltaN maxValueForFacoltaN
How can I take only the max value for every facolta?
Presumably, you just want:
SELECT F.nome, sum(orelez) AS oreTotali
FROM Docenza D JOIN
Facolta F
ON F.id = D.id_facolta
GROUP BY F.nome;
I'm not sure what the SELECT DISTINCT is supposed to be doing. It is almost never used with GROUP BY. The . . . suggests that you are selecting additional columns, which are not needed for the results you want.
This is untested, and since you didn't provide sample data with expected results I can't be sure it's really what you need.
It's a bit ugly and I'm sure there is some clever correlated sub query approach, but I've never been good with those.
SELECT st.focolta,
s_orelez,
TMP3.id_docenza
FROM some_table AS st
INNER
JOIN (SELECT *
FROM (SELECT focolta,
s_orelez,
id_docenza,
ROW_NUMBER() OVER -- Get the ranking of the orelez sum by focolta.
( PARTITION BY focolta
ORDER BY s_orelez DESC
) rn_orelez
FROM (SELECT focolta,
id_docenza,
SUM(orelez) OVER -- Sum the orelez by focolta
( PARTITION BY focolta
) AS s_orelez
FROM some_table
) TMP
) TMP2
WHERE = TMP2.rn_orelez = 1 -- Limit to the highest rank value
) TMP3
ON some_table.focolta = TMP3.focolta; -- Join to focolta to the id associated with the hightest value.

Grouping records on consecutive dates

If I have following table in Postgres:
order_dtls
Order_id Order_date Customer_name
-------------------------------------
1 11/09/17 Xyz
2 15/09/17 Lmn
3 12/09/17 Xyz
4 18/09/17 Abc
5 15/09/17 Xyz
6 25/09/17 Lmn
7 19/09/17 Abc
I want to retrieve such customer who has placed orders on 2 consecutive days.
In above case Xyz and Abc customers should be returned by query as result.
There are many ways to do this. Use an EXISTS semi-join followed by DISTINCT or GROUP BY, should be among the fastest.
Postgres syntax:
SELECT DISTINCT customer_name
FROM order_dtls o
WHERE EXISTS (
SELEST 1 FROM order_dtls
WHERE customer_name = o.customer_name
AND order_date = o.order_date + 1 -- simple syntax for data type "date" in Postgres!
);
If the table is big, be sure to have an index on (customer_name, order_date) to make it fast - index items in this order.
To clarify, since Oto happened to post almost the same solution a bit faster:
DISTINCT is an SQL construct, a syntax element, not a function. Do not use parentheses like DISTINCT (customer_name). Would be short for DISTINCT ROW(customer_name) - a row constructor unrelated to DISTINCT - and just noise for the simple case with a single expression, because Postgres removes the pointless row wrapper for a single element automatically. But if you wrap more than one expression like that, you get an actual row type - an anonymous record actually, since no row type is given. Most certainly not what you want.
What is a row constructor used for?
Also, don't confuse DISTINCT with DISTINCT ON (expr, ...). See:
Select first row in each GROUP BY group?
Try something like...
SELECT `order_dtls`.*
FROM `order_dtls`
INNER JOIN `order_dtls` AS mirror
ON `order_dtls`.`Order_id` <> `mirror`.`Order_id`
AND `order_dtls`.`Customer_name` = `mirror`.`Customer_name`
AND DATEDIFF(`order_dtls`.`Order_date`, `mirror`.`Order_date`) = 1
The way I would think of it doing it would be to join the table the date part with itselft on the next date and joining it with the Customer_name too.
This way you can ensure that the same customer_name done an order on 2 consecutive days.
For MySQL:
SELECT distinct *
FROM order_dtls t1
INNER JOIN order_dtls t2 on
t1.Order_date = DATE_ADD(t2.Order_date, INTERVAL 1 DAY) and
t1.Customer_name = t2.Customer_name
The result you should also select it with the Distinct keyword to ensure the same customer is not displayed more than 1 time.
For postgresql:
select distinct(Customer_name) from your_table
where exists
(select 1 from your_table t1
where
Customer_name = your_table.Customer_name and Order_date = your_table.Order_date+1 )
Same for MySQL, just instead of your_table.Order_date+1 use: DATE_ADD(your_table.Order_date , INTERVAL 1 DAY)
This should work:
SELECT A.customer_name
FROM order_dtls A
INNER JOIN (SELECT customer_name, order_date FROM order_dtls) as B
ON(A.customer_name = B.customer_name and Datediff(B.Order_date, A.Order_date) =1)
group by A.customer_name

Combining two nested SELECT with DISTINCT?

How do I look up DISTINCT values of one table, look for each name in another table and get both the values and their names as a result?
The Beleg table looks like this:
SELECT DISTINCT Ursprungskonto FROM Beleg
WHERE YEAR ( Valuta ) = 2016
gets me:
1000
1210
1220
1230
For each of these values, I need to lookup its name:
SELECT Name FROM Geldkonto
WHERE Kontonr = 1000
results in:
Kasse
At the end of the query, I need to have this result:
1000 Kasse
1210 OneBankName
1220 AnotherBankName
1230 YABN
I'm using SQL-92 (Filemaker).
Thanks a lot for any help!
You could try sub-query:
SELECT Kontonr , Name FROM Geldkonto
WHERE Kontonr in (SELECT DISTINCT Ursprungskonto FROM Beleg
WHERE YEAR ( Valuta ) = 2016)
You could use inner join
SELECT DISTINCT Beleg.Ursprungskonto, Geldkonto.Name
FROM Beleg
INNER JOIN Geldkonto
ON Beleg.Ursprungskonto=Geldkonto.Kontonr;
Instead of applying DISTINCT after the join you better do it before:
SELECT k.Kontonr, k.Name
FROM Geldkonto AS k
JOIN
(
SELECT DISTINCT Ursprungskonto
FROM Beleg
WHERE YEAR ( Valuta ) = 2016
) AS b
ON k.Kontonr = b.Ursprungskonto
This is similar to #rev_dihazum's solution, simply using a join instead of a subquery, useful if you need any additional columns from Beleg.

How do I get the top 10 results of a query?

I have a postgresql query like this:
with r as (
select
1 as reason_type_id,
rarreason as reason_id,
count(*) over() count_all
from
workorderlines
where
rarreason != 0
and finalinsdate >= '2012-12-01'
)
select
r.reason_id,
rt.desc,
count(r.reason_id) as num,
round((count(r.reason_id)::float / (select count(*) as total from r) * 100.0)::numeric, 2) as pct
from r
left outer join
rtreasons as rt
on
r.reason_id = rt.rtreason
and r.reason_type_id = rt.rtreasontype
group by
r.reason_id,
rt.desc
order by r.reason_id asc
This returns a table of results with 4 columns: the reason id, the description associated with that reason id, the number of entries having that reason id, and the percent of the total that number represents.
This table looks like this:
What I would like to do is only display the top 10 results based off the total number of entries having a reason id. However, whatever is leftover, I would like to compile into another row with a description called "Other". How would I do this?
with r2 as (
...everything before the select list...
dense_rank() over(order by pct) cause_rank
...the rest of your query...
)
select * from r2 where cause_rank < 11
union
select
NULL as reason_id,
'Other' as desc,
sum(r2.num) over() as num,
sum(r2.pct) over() as pct,
11 as cause_rank
from r2
where cause_rank >= 11
As said above Limit and for the skipping and getting the rest use offset... Try This Site
Not sure about Postgre but SELECT TOP 10... should do the trick if you sort correctly
However about the second part: You might use a Right Join for this. Join the TOP 10 Result with the whole table data and use only the records not appearing on the left side. If you calculate the sum of those you should get your "Sum of the rest" result.
I assume that vw_my_top_10 is the view showing you the top 10 records. vw_all_records shows all records (including the top 10).
Like this:
SELECT SUM(a_field)
FROM vw_my_top_10
RIGHT JOIN vw_all_records
ON (vw_my_top_10.Key = vw_all_records.Key)
WHERE vw_my_top_10.Key IS NULL