convert a subquery to over - sql

You can convert this query for example to optimize over for a consultation, is to get you up depending on the version, it's the same table.
select MAX(y.Version),
y.Unidad,
y.RutCompañia,
y.Cobertura,
y.Temporada,
y.PorcentajeSubsidio,y.RendimientoInferior,y.RendimientoSuperior
from ddbb.dbo.NS_CA y
where y.IDVariedad='010101' and y.Temporada ='2011'
and y.ZHS='CA0607'
and y.Moneda='UF'
and y.Version in (select MAX(x.Version) from ddbb.dbo.NS_CA x where x.IDVariedad='010101' and x.Temporada ='2011'
and x.ZHS='CA0607'
and x.Moneda='UF')
group by y.Unidad,y.RutCompañia,y.Cobertura,y.Temporada,y.PorcentajeSubsidio,y.RendimientoInferior,y.RendimientoSuperior
I imagined something like this
select MAX(b.Version) OVER(PARTITION BY b.IDVariedad,b.IDRubro ) as maximo
but it does not work properly
Thank you.
EDIT:
Thanks for you translate and answers.
Add more info , by example i have next table (tabla) :
| Version | Temporada | Unidad | etc |
|:-----------|------------:|:------------:|:------------:|
| 00 | 2011 | N | xx |
| 00 | 2011 | N | xx |
| 01 | 2011 | N | xx |
| 02 | 2011 | N | xx |
| 03 | 2011 | N | xx |
| 03 | 2011 | N | xx |
and query i would generate is :
select * from tabla a
where a.version in (select max(b.Version) from tabla b where b.Temporada='2011')
| Version | Temporada | Unidad | etc |
| 03 | 2011 | N | xx |
| 03 | 2011 | N | xx |
is possibility change sub-query to 'over partition' ? , thanks

WITH cte as
(SELECT ROW_NUMBER() OVER (ORDER by y.Version DESC) row_id
, y.Unidad
, y.RutCompañia
, y.Cobertura
, y.Temporada
, y.PorcentajeSubsidio
, y.RendimientoInferior
, y.RendimientoSuperior
FROM ddbb.dbo.NS_CA y
WHERE y.IDVariedad = '010101'
AND y.Temporada = '2011'
AND y.ZHS = 'CA0607'
AND y.Moneda = 'UF'
AND y.Version)
SELECT * FROM cte
where row_id = 1

Seems you want this:
select MAX(y.Version),
y.Unidad,
y.RutCompañia,
y.Cobertura,
y.Temporada,
y.PorcentajeSubsidio,y.RendimientoInferior,y.RendimientoSuperior
from y.Version =
(select MAX(x.Version)
from ddbb.dbo.NS_CA x
where x.IDVariedad=y.IDVariedad and x.Temporada = y.Temporada
and x.ZHS=y.ZHS and x.Moneda=y.Moneda)
group by y.Unidad,y.RutCompañia,y.Cobertura,y.Temporada,y.PorcentajeSubsidio,y.RendimientoInferior,y.RendimientoSuperior
See Why no windowed functions in where clauses?

Related

SQL - Create a distinct list based on datetime

I'm trying to retrieve a list of results where there are multiple rows which have a duplicate field in different rows but only want to retrieve the row which has been created most recently
Data
loc | created | dest | w | l | h
--------------------------------------------------------------------
2 | 2020/11/09 07:00:00 | north | 12 | 10 | 34
3 | 2020/11/09 07:10:00 | south | 34 | 67 | 23
3 | 2020/11/09 08:13:00 | west | 67 | 22 | 12
I have tried the the following which does give me the rows I require but is missing the extra columns which I require.
Select loc, MAX(created)
from Data
Group By loc
Results Required
loc | created | dest | w | l | h
--------------------------------------------------------------------
2 | 2020/11/09 07:00:00 | north | 12 | 10 | 34
3 | 2020/11/09 08:13:00 | west | 67 | 22 | 12
Try this:
select *
from (
select *, row_number() over (partition by loc order by created desc) rn
from Data
) t
where rn=1
with max_vals as (
Select loc, MAX(created) as max_created
from Data
Group By loc
)
select d.*
from Data d join max_vals m
on d.loc = m.loc and d.created = m.max_created

SQL Query in MSAccess to Rank a Value Column with Letters based on it's Sort Order

I am trying to write an SQL Query on a table in MSAccess to add a virtual Column that will add sequential Letters of the Alphabet based on a Value column sorted in Descending order.
------------------------------------------------
| Filename | Zone | ValueCol |
------------------------------------------------
| abc | Zone_MEA | 33 |
| abc | Zone_DEA | 29 |
| abc | Zone_SEO | 21 |
| abc | Zone_GUY | 09 |
|-----------------------------------------------
| def | Zone_SEO | 30 |
| def | Zone_DEA | 22 |
| def | Zone_MEA | 07 |
| def | Zone_GUY | 06 |
|----------------------------------------------|
| ghi | Zone_GUY | 21 |
| ghi | Zone_MEA | 12 |
| ghi | Zone_SEO | 04 |
| ghi | Zone_DEA | 04 |
------------------------------------------------
So all values in ValueCol sorted in descending order will receive a sequential letter starting from A per Zone set.
Virtual Col
---------------------------------------------------------------
| Filename | Zone | ValueCol | Letter |
---------------------------------------------------------------
| abc | Zone_MEA | 33 | A |
| abc | Zone_DEA | 29 | B |
| abc | Zone_SEO | 21 | C |
| abc | Zone_GUY | 09 | D |
|-------------------------------------------------------------|
| def | Zone_SEO | 30 | A |
| def | Zone_DEA | 22 | B |
| def | Zone_MEA | 07 | C |
| def | Zone_GUY | 06 | D |
|-------------------------------------------------------------|
| ghi | Zone_GUY | 21 | A |
| ghi | Zone_MEA | 12 | B |
| ghi | Zone_SEO | 04 | C |
| ghi | Zone_DEA | 04 | D |
---------------------------------------------------------------
Is there a way to write such an SQL query in MSAccess without resorting to creating any physical helper tables? (Exception maybe a virtual helper table, but don't know how to create one or how it may be used.)
EDIT: Each section is one particular filename.
Wrote this query on suggestions from #Erik A. Here's the query:
SELECT M.FILENAME, M.ZONE,M.[VALUECOL],
CHR(64 + (
SELECT COUNT(*)
FROM tblTest AS S
WHERE
S.[FILENAME] = M.[FILENAME]
AND S.[ZONE] <= M.[ZONE]
AND S.[VALUECOL] <= M.[VALUECOL]
AND S.[FILENAME]&S.[ZONE]&S.[VALUECOL]<=M.[FILENAME]&M.[ZONE]&M.[VALUECOL]
) ) AS POS
FROM tblTest AS M
GROUP BY M.[FILENAME], M.[ZONE], M.[VALUECOL]
ORDER BY M.[FILENAME] ASC, M.[VALUECOL] DESC,M.[ZONE] ASC
The Alphabetical order is still not sequential as can be seen in the below output.
Also getting duplicate letters within a particular FILENAME section.
Edit...once again:
This takes of point 2 i.e. Duplicates, but not point 1.
SELECT M.FILENAME, M.ZONE,M.[VALUECOL],
CHR(64 + (
SELECT COUNT(*)
FROM tblTest AS S
WHERE
S.[FILENAME] = M.[FILENAME]
AND S.[FILENAME]&S.[ZONE] <= M.[FILENAME]&M.[ZONE]
AND S.[FILENAME]&S.[ZONE]&S.[VALUECOL]<=M.[FILENAME]&M.[ZONE]&M.[VALUECOL]
) ) AS POS
FROM tblTest AS M
GROUP BY M.[FILENAME], M.[ZONE], M.[VALUECOL]
ORDER BY M.[FILENAME] ASC, M.[VALUECOL] DESC,M.[ZONE] ASC
A very general solution for a very general question:
If you have well-defined ordering (you order by a column that doesn't have duplicates) and grouping, you can use a subquery to achieve this:
It would look like this:
SELECT
(
SELECT COUNT(*)
From MyTable s
WHERE
s.GroupingColumn1 = m.GroupingColumn1
AND s.GroupingColumnN = m.GroupingColumnN
AND s.SortingColumn1 <= m.SortingColumn1
)
FROM MyTable m
GROUP BY GroupingColumn1, GroupingColumnN
ORDER BY SortingColumnN
That gets you the position of the items within the groups.
You can easily convert this to capital letters using a little knowledge of the ASCII table (A = position 65, capitals are all sequential, so by incrementing the position by 64 and looking up the ASCII character for the position, you'll get A for 1, B for 2, etc)
Chr(MyPosition + 64)
Of course, if the table is stored in a backend that supports window functions, this can be done more clearly, concisely, and faster. Unfortunately, Access does not support them.
Ordering should be implemented using > and <, which makes the statement fairly long for multiple ordering conditions:
SELECT M.[FILENAME], M.[ZONE],M.[VALUECOL],
CHR(64 + (
SELECT COUNT(*)
FROM tblTest AS S
WHERE
(S.[FILENAME] = M.[FILENAME])
AND (
(s.VALUECOL > m.VALUECOL)
OR (
(s.VALUECOL = m.VALUECOL) AND (s.ZONE <= m.ZONE)
)
)
) ) AS LETTER
FROM tblTest AS M
GROUP BY M.[FILENAME], M.[ZONE], M.[VALUECOL]
ORDER BY M.[FILENAME] ASC, M.[VALUECOL] DESC,M.[ZONE] ASC

Find the first key by date field using sql and output also have other fields

I want to query the first occurrence of every name according to the earliest date. The output should have the complete row. Please help me to write the query in sql.
Input:
Name | ID | payment_date | Pack
------+-------+-----------------+-------
A | 11 | 31-Jan | P
C | 13 | 31-Jan | Q
B | 2 | 31-Jan | R
C | 3 | 28-Jan | P
D | 23 | 29-Jan | Q
B | 11 | 30-Jan | R
A | 17 | 25-Jan | P
C | 13 | 26-Jan | Q
D | 17 | 2-Feb | R
B | 23 | 3-Feb | P
A | 45 | 4-Feb | Q
B | 3 | 5-Feb | R
Output:
Name | ID | payment_date | Pack
-----+-------+--------------+-----
A | 17 | 25-Jan | P
B | 11 | 30-Jan | R
C | 13 | 26-Jan | Q
D | 23 | 29-Jan | Q
You can use the min function, also assuming payment_date is a date type:
select Name, ID, min(payment_date), Pack from mytable
group by payment_date,Name, ID, Pack
order by Name
The downfall about this method is putting all of the fields in the group by.
If your payment_date is a date data type, you can use not exists() like so:
select *
from t
where not exists (
select 1
from t i
where i.Name = t.Name
and i.payment_date < t.payment_date
)
rextester demo (sql server): http://rextester.com/OKB46268
returns
+------+----+-------------+------+
| Name | Id | PaymentDate | Pack |
+------+----+-------------+------+
| A | 17 | 2017-01-25 | P |
| B | 11 | 2017-01-30 | R |
| C | 13 | 2017-01-26 | Q |
| D | 23 | 2017-01-29 | Q |
+------+----+-------------+------+
You can also use Vertica's enhanced LIMIT clause:
WITH
-- input, don't use in real query
input(Name,ID,payment_date,Pack) AS (
SELECT 'A',11,DATE '31-Jan-2017','P'
UNION ALL SELECT 'C',13,DATE '31-Jan-2017','Q'
UNION ALL SELECT 'B',2, DATE '31-Jan-2017','R'
UNION ALL SELECT 'C',3, DATE '28-Jan-2017','P'
UNION ALL SELECT 'D',23,DATE '29-Jan-2017','Q'
UNION ALL SELECT 'B',11,DATE '30-Jan-2017','R'
UNION ALL SELECT 'A',17,DATE '25-Jan-2017','P'
UNION ALL SELECT 'C',13,DATE '26-Jan-2017','Q'
UNION ALL SELECT 'D',17,DATE '2-Feb-2017','R'
UNION ALL SELECT 'B',23,DATE '3-Feb-2017','P'
UNION ALL SELECT 'A',45,DATE '4-Feb-2017','Q'
UNION ALL SELECT 'B',3, DATE '5-Feb-2017','R'
)
-- end of input , start real query here:
SELECT * FROM input
LIMIT 1 OVER(PARTITION BY Name ORDER BY payment_date)
;
Happy playing ...
Marco the Sane

Using ORDER BY and getting most recent version of records

I have a database structured in the following way
ID | DATE | col_0 |
--------------------------
1 | 2014 | A_Ver2_data0 |
2 | 2014 | A_Ver2_data1 |
3 | 2014 | A_Ver2_data2 |
4 | 2013 | A_Ver1_data0 |
5 | 2013 | A_Ver1_data1 |
6 | 2012 | A_Ver0_data0 |
7 | 2012 | A_Ver0_data1 |
8 | 2013 | B_Ver3_data0 |
9 | 2013 | B_Ver3_data1 |
10 | 2013 | B_Ver3_data2 |
11 | 2010 | B_Ver2_data0 |
12 | 2010 | B_Ver2_data1 |
13 | 2009 | B_Ver1_data0 |
14 | 2007 | B_Ver0_data0 |
I need to write a query that will return the most recent version of the A_ and B_ prefixed data sets. So I was thinking something like SELECT * FROM db.table ORDER BY DATE DESC But I want to filter out expired versions. desired output should be:
ID | DATE | col_0 |
--------------------------
1 | 2014 | A_Ver2_data0 |
2 | 2014 | A_Ver2_data1 |
3 | 2014 | A_Ver2_data2 |
8 | 2013 | B_Ver3_data0 |
9 | 2013 | B_Ver3_data1 |
10 | 2013 | B_Ver3_data2 |
Any Ideas?
I think this does what you want. It parses the column to get the first and last parts and then finds the maximum "DATE" for each. It returns the row that matches the date:
select id, "DATE", COL_A
from (select v.*,
max("DATE") over (partition by substr(col_A, 1, 1),
substr(col_A, 8)
) as maxdate
from versiones v
) v
where "DATE" = maxdate;
The SQL Fiddle is here.
I am not sure but i think this would work : "HAVING date >= MAX(date)-1"
max(date)-1 will return 2014-1 = 2013 , which will eventually filter out the results based on date >= 2013 .
But this would list all the 2013,2014 entries ..
You could use an analytic function to get the maximum version, and then select the corresponding records, as below:
SELECT
*
FROM
db.table
WHERE
Col_0 IN
(
Select Distinct
Max(Col_0) Over (Partition By Replace(Col_0, Replace(Regexp_Substr(Col_0, '_[^,]+_'), '_', ''), '')
Order By REPLACE(Regexp_Substr(Col_0, '_[^,]+_'), '_', '') DESC) AS Col_0
FROM
db.table
);
Also, please note that you would not be able to name a column as DATE, because DATE is a reserved word.
Here is my answer:
select * from
versiones
where SUBSTR(COL_A,0,6)
in(
select version from
(
select SUBSTR(COL_A,0,1) letra,max(SUBSTR(COL_A,6,1)) maximo,
SUBSTR(COL_A,0,1)||'_Ver'||max(SUBSTR(COL_A,6,1)) version
from versiones
group by SUBSTR(COL_A,0,1)
)
cs
)
Sqlfiddle: http://sqlfiddle.com/#!4/84a8f/13

Sum of count from external table

I need to search for the sum of the games made by specific developers. I have two tables:
_____________________________
|____________GAMES____________|
| Id | Title | id_dev | hits |
| 01 | abc | 1 | 20 |
| 02 | xyz | 2 | 15 |
| 03 | cde | 1 | 9 |
_______________
|__DEVELOPERS___|
| Id | Title |
| 01 | poi |
| 02 | asd |
| 03 | qwe |
I want result formatted like Developers title 40, where 40 is the sum of all hits of the games with the ID of this developer. How can I go about this?
SELECT developers.title, COUNT(count) AS total FROM (SELECT COUNT(games.hits) AS count
FROM games
GROUP BY id_dev
HAVING count > 1) as A
FROM developers
JOIN games
WHERE developers.id = games.id_dev
This is a simple join and aggregate, so you are overcomplicating things:
select d.id, d.title, sum(g.hits)
from games g join
developers d
on g.id_dev = d.id
group by d.id, d.title;