sql subquery statement with a join - sql

i am not able to think on how to build an sql statement that fits my needs, if this is just not possibl eplease let me know.
i have a table like this(ill ommit the columns not making dificulties to me)
table: servicio
------------------------------------------------------------
id | swo | date | issueValue |
1 | 15-001 | 2015-01-29 01:52:59 | 2
1 | 15-002 | 2015-01-30 01:24:00 | 2
------------------------------------------------------------
table: comments
------------------------------------------------------------
id | swo | date | Area |
1 | 15-001 | 2015-01-29 01:52:59 | 2
1 | 15-002 | 2015-01-30 01:24:00 | 1
1 | 15-002 | 2015-01-30 01:50:00 | 3
------------------------------------------------------------
i want to select the rows in servicio but include the latest Area assigned to each swo. the result should be something like this.
------------------------------------------------------------
id | swo | date | Area |
1 | 15-001 | 2015-01-29 01:52:59 | 2
1 | 15-002 | 2015-01-30 01:24:00 | 3
------------------------------------------------------------
so how can i make the sql statement check for top (1) and return the area value in it?

In SQL Server you can do this with apply, a subquery, or row_number(). Here is the first method:
select s.*, c.area
from servicio s outer apply
(select top 1 c.*
from comments c
where c.swo = s.swo
order by c.date desc
) c;
With this method, you can pull out additional columns if you like.

This is solution with Rownumber and subquery. In subquery you are selecting all rows which match between tables. Creating Ascending order for Comment table date column, Partitioning for every SWO (Partitioning means it will reset counter on every different SWO). 1 = Last date for particular SWO. In final select you just put WHERE clause with Rownum = 1
SELECT swo ,date ,issueValue ,Area
FROM
(SELECT
SRV.swo
,SRV.date
,SRV.issueValue
,CMNT.Area
,ROW_NUMBER() OVER (PARTITION BY CMNT.swo ORDER BY CMNT.date DESC) AS Dsc_Rank
FROM servicio AS SRV
LEFT JOIN comments AS CMNT
ON (SRV.swo=CMNT.swo)
) AS Temp
WHERE Temp.Dsc_Rank = 1 /* Descending Date Rank 1 = Latest date from Comment table*/
Note: for example in Teradata you can omit sub query and use Qualify clause

Related

How can I create time range grouping in window function SQL

I'm trying to create a grouping using multiple window function on SQL, the objective is to discern between different groups if there are some other groups in the middle. see below table
Part | time | expected result |
a | 11-29-2022 00:05:00.000 | 1 |
a | 11-29-2022 00:05:00.010 | 1 |
b | 11-29-2022 00:06:00.000 | 2 |
c | 11-29-2022 00:15:00.000 | 3 |
c | 11-29-2022 00:15:00.000 | 3 |
b | 11-29-2022 00:40:00.010 | 4 |
b | 11-29-2022 00:40:00.020 | 4 |
b | 11-29-2022 00:40:00.020 | 4 |
b | 11-29-2022 00:40:00.030 | 4 |
I'm doing something like:
Select part, time, count(*) over(Partition by Part order by time )
Lets focus in part "b", first occurrence is at minute 6, after that appears different parts and part b appears again at minute 40 so I need something like a time range to create the grouping
Also notice that sometimes the time is different in milliseconds even if the parts are consecutive (part b), those must belong to the same group.
Was trying to use the Rank window function but with 'range between' wasn't able to get that result.
Thanks!
Just another option via dense_rank()
Select *
,NewValue = dense_rank() over (order by convert(varchar(25),[Time],120))
From YourTable
Results
Please try this sql query.
Select part, time, dense_rank() over(Partition by Part )
or
Select part, time, dense_rank() over(Partition by Part order by time rows between unbounded preceding and unbounded following )

Comparing two tables that are the same and listing out the max date

I was wondering if it's possible to compare dates within the same table with same ID, but the catch is that there is an additional column that display the status. For instance, here's a table A:
The results I would like to see is this:
I know I could use a group by and max aggregate with ID to find the max date; however, I would like the status (Running/Stopped) column associated to be there. It would help me a lot.
In most databases, the fastest method (assuming the right indexes) is a correlated subquery:
select t.*
from t
where t.date = (select max(t2.date) from t t2 where t2.id = t.id);
Even if not the fastest, this should work in any database.
In case of Oracle, you can use the KEEP clause like this:
SELECT t.id,
MAX(t.status) KEEP (DENSE_RANK LAST ORDER BY t."DATE") AS corresponding_status,
MAX(t."DATE") AS last_date
FROM tab t
GROUP BY t.id
ORDER BY 1
For this sample data:
+----+---------+------------+
| ID | STATUS | DATE |
+----+---------+------------+
| 1 | Running | 2018-02-03 |
| 1 | Stopped | 2018-04-04 |
| 2 | Running | 2018-03-24 |
| 2 | Stopped | 2018-01-02 |
| 3 | Running | 2018-06-12 |
| 3 | Stopped | 2018-06-12 |
+----+---------+------------+
This would return this result:
+----+----------------------+------------+
| ID | CORRESPONDING_STATUS | LAST_DATE |
+----+----------------------+------------+
| 1 | Stopped | 2018-04-04 |
| 2 | Running | 2018-03-24 |
| 3 | Stopped | 2018-06-12 |
+----+----------------------+------------+
As can be seen in this SQL Fiddle.
For the cases, when you have multiple entries on the same ID and DATE combination, it'll choose one STATUS value - in this case the last one (based on alphanumerical sorting), as I've used MAX on the STATUS.
The part LAST ORDER BY t."DATE" corresponds to how we choose DATE value in the group, i.e. by choosing the last DATE in the group.
See this Oracle Docs entry on more details.

ORDER BY FIELD LIST - Subquery returns more than 1 row

What i want to do is quite simple:
Write an SQL that will return a bunch of record and order the records by some list of id from the FIELD LIST section of my SQL
TABLE SAMPLE
lessons
+----+----------------------+
| id | name |
+----+----------------------+
| 9 | Greedy algorithms |
| 5 | Maya civilization |
| 3 | eFront Beginner |
| 2 | eFront Intermediate |
+----+----------------------+
mod_comp_rule
+----+---------------------+
| id | lesson_id | comp_id |
+----+---------------------+
| 1 | 3 | 1 |
| 2 | 2 | 1 |
| 3 | 9 | 2 |
+----+---------------------+
WHAT I WANT TO GET FROM MY QUERY
SELECT * FROM lessons ORDER BY FIELD(id,'3','2','9') ASC;
MY SQL
SELECT ls.id, ls.name
FROM lessons ls
ORDER BY FIELD(ls.id,
(SELECT mcr.lesson_id FROM mod_comp_rule mcr
INNER JOIN lessons ls ON ls.id = mcr.lesson_id))
My SQL Query returned the following error
MySQL said: #1242 - Subquery returns more than 1 row
So how can i make my SQL return FIELD(id,'3','2','9') without flagging the more than 1 row error ?
I don't see why FIELD() is needed for this. A correlated query will do what you want:
SELECT ls.id, ls.name
FROM lessons ls
ORDER BY (SELECT mcr.id FROM mod_comp_rule mcr WHERE ls.id = mcr.lesson_id);

Filter same Value - Only take One - Without using CTE

I have the following table (which i get after a simple date filter Query)
now i want to Show the SalesAmount for the given Month, though i have Multiple values for the same ID in some cases, in which case, one Needs to be ignored and the other taken, otherwise (if there is only one entry for the ID, that value should be taken)
| TableID |SalesOpportunityID| OrderID | SalesCycleStatus | Status | SalesAmount | OrderIncMonth |
| 1 | 14551 | NULL | Oppurtunity | Lost | 1556,316 | 2013-10 |
| 2 | 12551 | 14515 | Order | Active | 23563,23 | 2013-10 |
| 3 | 12151 | 15131 | Order | Active | 2212352,43 | 2013-10 |
| 4 | 14531 | NULL | Opportunity | Lost | 32152332,1 | 2013-10 |
| 5 | 12651 | NULL | Opportunity | Won | 23525,3245| 2013-10 |
| 6 | 12651 | 21452 | Order | Active | 23525,3245| 2013-10 |
As you can See, TableID = 5 and = 6 are basicaly the same Order,
just stored as a Sales Opurtunity, than (after it was won) turned into an Order and Stored again,
since i am trying to Calculate the Summ of all Oportunities and Orders for the Month being,
i Need to filter out these Double Values, and do so by: Taking only the Value for the SalesOpportunityID where the SalesCycleStatus is = Order
(IF of curse there are 2 entries with the same SalesOpportunityID, Otherwise take SalesAmount for SalesOpportunityID - regardles weather the SalesCycleStatus is an Order or an Opportunity)
Is there a way to do this without using CTE ?
thanks a lot for your Help :) !
You can also achieve this using ROW_NUNBER() which could be more efficient as it only requires one table scan:
SELECT TableID,
SalesOpportunityID,
OrderID,
SalesCycleStatus,
Status,
SalesAmount
FROM ( SELECT TableID,
SalesOpportunityID,
OrderID,
SalesCycleStatus,
Status,
SalesAmount ,
RowNumber = ROW_NUMBER() OVER(PARTITION BY SalesOpportunityID
ORDER BY CASE WHEN SalesCycleStatus = 'Order' THEN 1 ELSE 0 END, TableID)
FROM T
) t
WHERE t.RowNumber = 1;
There are a number of ways to do this: the first one I'd suggest would be a subquery using NOT EXISTS as follows:
SELECT TableID, SalesOpportunityID, OrderID, SalesCycleStatus, Status, SalesAmount
FROM TestTable
WHERE
SalesCycleStatus = 'Order' OR
NOT EXISTS
(
SELECT TableID
FROM TestTable sub
WHERE
SalesCycleStatus = 'Order' AND
sub.SalesOpportunityID = TestTable.SalesOpportunityID
)
Click here for the full SQL Fiddle example

SQL: Update column with with an index that resets if column changes

Firstly, sorry for the wording of the question. I'm not too sure how to express it. Hopefully the example below is clear.
If I have a table
Id | Type | Order
0 | Test | null
1 | Test | null
2 | Blah | null
3 | Blah | null
I want to turn it into this
Id | Type | Order
0 | Test | 1
1 | Test | 2
2 | Blah | 1
3 | Blah | 2
So I'm grouping the table by 'type' and allocating a number to 'order' incrementally. It'll start at 1 per type.
How should I go about doing it?
Db I'm using is Sybase 15.
select
Id,
Type,
row_number() over(partition by Type order by Id) as [Order]
from YourTable
You should utilize the ROW_NUMBER function to get you what you're looking for.
ROW_NUMBER (Transact-SQL)
Returns the sequential number of a row within a partition of a result
set, starting at 1 for the first row in each partition.