SQL - how to select data on a monthly basis? - sql

I have the following DF :
Date
Name
NumberID
05-JAN-2022
Test
1
07-JAN-2022
Test
1
09-FEB-2022
Test
1
10-FEB-2022
Test
1
11-FEB-2022
Test
1
12-MAR-2022
Test
1
02-MAR-2022
Test
1
What would be the SQL Request which would return :
Date
Name
NumberID
05-JAN-2022
Test
1
09-FEB-2022
Test
1
02-MAR-2022
Test
1
Note that I can't do a
SELECT * FROM Tab1 tb1 WHERE tb1.Date in ('05-JAN-2022','09-FEB-2022',ect..)
Because there is a ton of dates and I don't know them.
More complex example
Date
Name
NumberID
05-JAN-2022
Test
1
07-JAN-2022
Test
1
09-FEB-2022
Test
1
10-FEB-2022
Test
1
11-FEB-2022
Test1
1
12-MAR-2022
Test
1
02-MAR-2022
Test
1
the output would be :
Date
Name
NumberID
05-JAN-2022
Test
1
09-FEB-2022
Test
1
11-FEB-2022
Test1
1
02-MAR-2022
Test
1
My Query :
SELECT
tc.TITLETest,
MIN(tc.DATETest) AS YOURDATE,
tc.CRSTest
FROM Table tc
WHERE tc.TC= 1
GROUP BY EXTRACT(MONTH FROM YOURDATE),tc.TITLETest,tc.CRSTest
ORDER BY YOURDATE

We can select the earliest date with MIN and GROUP BY the month:
SELECT MIN(yourdate) AS yourdate, name, numberid
FROM yourtable
GROUP BY EXTRACT(MONTH FROM yourdate), name, numberid
ORDER BY yourdate;
Using SQL key words as table name or column name is a bad idea, so the column "date" should be renamed to something more meaningful, for example "sellDate".
This query produces this result for your sample data:
Yourdate
Name
NumberID
05-JAN-2022
Test
1
09-FEB-2022
Test
1
11-FEB-2022
Test1
1
02-MAR-2022
Test
1
Try out here: db<>fiddle

We can use ROW_NUMBER here:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY TO_CHAR("Date", 'MON-YYYY')
ORDER BY "Date") rn
FROM yourTable t
)
SELECT "Date", Name, NumberID
FROM cte
WHERE rn = 1;

Related

update Query for “Shifting row value down for one column” ( postgresql)

I have 10000 rows like this table
id name gradeId
------------------------------------------
1327 test0 1
1330 test1 1
1331 test2 1
i want to update the name column (Shifting values down for name column) I want the Result Like this
id name gradeId
------------------------------------------
1327 test1 1
1330 test2 1
1331 test3 1
You can use lead():
update t
set name = tt.next_name
from (select t.*, lead(name) over (order by id) as next_name
from t
) tt
where tt.id = t.id;
Of course, the last name will be set to NULL.
To put in a fixed value for the last row, just use a default value for lead():
update t
set name = tt.next_name
from (select t.*,
lead(name, 1, <default value>) over (order by id) as next_name
from t
) tt
where tt.id = t.id;
You need to split the number from the text, cast it to number and add 1, then cast it back to string and concat it to the original text.
UPDATE grades
SET name = CONCAT('test', CAST(CAST(SUBSTRING(name, 5) AS INTEGER) + 1 AS TEXT));

Non duplicate records with max date query on oracle

Hello i have a problem with a simple query. I need to see the max date of some articles in two direfent sites.
This is my actual query:
SELECT a.aa_codart, MAX(t.tr_fechafac), t.tr_tipo
FROM ARTALM a, traspaso t
WHERE t.tr_codart = a.aa_codart
and t.tr_tipomov > 1
and a.aa_codalm = '1'
and (t.tr_tipo >= 1 and t.tr_tipo <=2)
group by a.aa_codart, t.tr_tipo;
And the result:
01..FRB10X80 30/11/07 2
01..FRB10X80 08/03/01 1
01.32122RS 05/02/16 1
01.32122RS 02/07/10 2
01.33052Z 21/09/15 1
01.60042Z 24/02/16 2
I want, for example in the two first rows, see only one row, like this:
01..FRB10X80 30/11/07 2
01.32122RS 05/02/16 1
01.33052Z 21/09/15 1
01.60042Z 24/02/16 2
Taking the max date
Thanks
This calls for an analytical query. This query shows how the ROW_NUMBER() function will assign the value 1 to the row with the article's most recent date. Give it a try first to help understand the final query, coming up next:
SELECT
a.aa_codart,
t.tr_fechafac,
t.tr_tipo,
ROW_NUMBER() OVER (PARTITION BY a.aa_codart ORDER BY t.tr_fechafac DESC) as rnk
FROM artalm a
INNER JOIN trapaso t ON a.aa_codart = t.tr_codart
WHERE t.tr_tipomov > 1
AND a.aa_codalm = '1'
AND t.tr_tipo BETWEEN 1 AND 2
You can't apply the WHERE clause to the rnk column because the column is calculated after the WHERE clause. You can get around this using a nested query:
SELECT * FROM (
SELECT
a.aa_codart,
t.tr_fechafac,
t.tr_tipo,
ROW_NUMBER() OVER (PARTITION BY a.aa_codart ORDER BY t.tr_fechafac DESC) as rnk
FROM artalm a
INNER JOIN trapaso t ON a.aa_codart = t.tr_codart
WHERE t.tr_tipomov > 1
AND a.aa_codalm = '1'
AND t.tr_tipo BETWEEN 1 AND 2
) WHERE rnk = 1;
I apologize in advance for any column names I may have retyped badly. The Oracle syntax should be fine; the column names maybe not so much :)
I think you may want to look at row_number() (then just pick the ones where it is one) something like this.
WITH t
AS (SELECT 'A' aa_codart,
TO_DATE ('17/05/00', 'dd/mm/yy') mydt,
1 tr_tipo
FROM DUAL
UNION ALL
SELECT 'A', TO_DATE ('12/04/00', 'dd/mm/yy'), 2 FROM DUAL
UNION ALL
SELECT 'B', TO_DATE ('30/06/98', 'dd/mm/yy'), 2 FROM DUAL
UNION ALL
SELECT 'C', TO_DATE ('30/06/98 ', 'dd/mm/yy'), 2 FROM DUAL),
t2
AS (SELECT aa_codart,
mydt,
tr_tipo,
ROW_NUMBER ()
OVER (PARTITION BY aa_codart ORDER BY mydt DESC)
rn
FROM t)
SELECT *
FROM t2
WHERE rn = 1

Case statement not supporting horizontal search with column name in query

I am new to ORACLE SQL and I am trying to learn it quickly.
I have following table definition:
Create table Sales_Biodata
(
Saler_Id INTEGER NOT NULL UNIQUE,
Jan_Sales INTEGER NOT NULL,
Feb_Sales INTEGER NOT NULL,
March_Sales INTEGER NOT NULL
);
Insert into Sales_Biodata (SALER_ID,JAN_SALES,Feb_Sales,March_Sales)
values ('101',22,525,255);
Insert into Sales_Biodata (SALER_ID,JAN_SALES,Feb_Sales,March_Sales)
values ('102',22,55,25);
Insert into Sales_Biodata (SALER_ID,JAN_SALES,Feb_Sales,March_Sales)
values ('103',45545,5125,2865);
My objective is the following:
1- Searching the highest sales and second highest sales against each saler_id.
For example in our above case:
For saler_id =101 highest sales is 525 and second highest sales is 255
similary for saler_id=102 highest sales is 55 and second highest sales is 25
For my above approach I am using the following query:
Select Saler_Id,
(
CASE
WHEN JAN_SALES>FEB_SALES AND JAN_SALES>MARCH_SALES THEN JAN_SALES
WHEN FEB_SALES>JAN_SALES AND FEB_SALES>MARCH_SALES THEN FEB_SALES
WHEN MARCH_SALES>JAN_SALES AND MARCH_SALES>FEB_SALES THEN MARCH_SALES
WHEN JAN_SALES=FEB_SALES AND JAN_SALES=MARCH_SALES THEN JAN_SALES
WHEN JAN_SALES=FEB_SALES AND JAN_SALES>MARCH_SALES THEN JAN_SALES
WHEN JAN_SALES=MARCH_SALES AND JAN_SALES>FEB_SALES THEN JAN_SALES
WHEN FEB_SALES=JAN_SALES AND FEB_SALES>MARCH_SALES THEN FEB_SALES
WHEN FEB_SALES=MARCH_SALES AND FEB_SALES>JAN_SALES THEN FEB_SALES
WHEN MARCH_SALES=JAN_SALES AND MARCH_SALES>FEB_SALES THEN MARCH_SALES
WHEN MARCH_SALES=FEB_SALES AND MARCH_SALES>JAN_SALES THEN MARCH_SALES
ELSE 'NEW_CASE_FOUND'
END
) FIRST_HIGHEST,
(
CASE
WHEN JAN_SALES>FEB_SALES AND FEB_SALES>MARCH_SALES THEN FEB_SALES
WHEN FEB_SALES>JAN_SALES AND JAN_SALES>MARCH_SALES THEN JAN_SALES
WHEN JAN_SALES>MARCH_SALES AND MARCH_SALES>FEB_SALES THEN MARCH_SALES
ELSE 'NEW_CASE_FOUND'
END
) SECOND_HIGHEST
from
Sales_Biodata;
but I am getting the following errors:
ORA-00932: inconsistent datatypes: expected NUMBER got CHAR
00932. 00000 - "inconsistent datatypes: expected %s got %s"
*Cause:
*Action:
Error at Line: 60 Column: 6
Please guide me on the following:
1- How to search the data horizontally for maximum and second maximum.
2- Please guide me on alternate approaches for searching data for a row horizontally.
Getting the maximum value is simply:
select greatest(jan_sales, feb_sales, mar_sales)
If you want the second value:
select (case when jan_sales = greatest(jan_sales, feb_sales, mar_sales)
then greatest(feb_sales, mar_sales)
when feb_sales = greatest(jan_sales, feb_sales, mar_sales)
then greatest(jan_sales, mar_sales)
else greatest(jan_sales, feb_sales)
end)
However, this is the wrong approach to the whole problem. The main issues is that you have the wrong data structure. Store values in rows not columns. So, you need to unpivot your data and re-aggregation, such as:
select saler_id,
max(case when seqnum = 1 then sales end) as sales_1,
max(case when seqnum = 2 then sales end) as sales_2,
max(case when seqnum = 3 then sales end) as sales_3
from (select s.*, dense_rank() over (partition by saler_id order by sales desc) as seqnum
from (select saler_id, jan_sales as sales Sales_Biodata union all
select saler_id, feb_sales Sales_Biodata union all
select saler_id, mar_sales Sales_Biodata
) s
) s
group by saler_id;
Your data model is wrong.
The first thing I would do is to unpivot data using this query:
select * from sales_biodata
unpivot (
val for mon in ( JAN_SALES,FEB_SALES,MARCH_SALES )
)
;
and after this, getting two top values is relatively easy:
SELECT *
FROM (
SELECT t.*,
dense_rank() over (partition by saler_id order by val desc ) x
FROM (
select * from sales_biodata
unpivot (
val for mon in ( JAN_SALES,FEB_SALES,MARCH_SALES )
)
) t
)
WHERE x <= 2
the above query will give a result in this format:
SALER_ID MON VAL X
---------- ----------- ---------- ----------
101 FEB_SALES 525 1
101 MARCH_SALES 255 2
102 FEB_SALES 55 1
102 MARCH_SALES 25 2
103 JAN_SALES 45545 1
103 FEB_SALES 5125 2
If you have more month than 3 months, you can easily extend this query changing this part:
val for mon in ( JAN_SALES,FEB_SALES,MARCH_SALES, April_sales, MAY_SALES, JUNE_SALES, JULY_SALES, ...... NOVEMBER_SALES, DECEMBER_SALES )
If you want both two values in one row, you need to pivot data back:
WITH src_data AS(
SELECT saler_id, val, x
FROM (
SELECT t.*,
dense_rank() over (partition by saler_id order by val desc ) x
FROM (
select * from sales_biodata
unpivot (
val for mon in ( JAN_SALES,FEB_SALES,MARCH_SALES )
)
) t
)
WHERE x <= 2
)
SELECT *
FROM src_data
PIVOT(
max(val) FOR x IN ( 1 As "First value", 2 As "Second value" )
);
This gives a result in this form:
SALER_ID First value Second value
---------- ----------- ------------
101 525 255
102 55 25
103 45545 5125
EDIT - why MAX is used in the PIVOT query
The short answer is: because the syntax reuires an aggregate function here.
See this link for the syntax: http://docs.oracle.com/cd/E11882_01/server.112/e41084/statements_10002.htm#CHDCEJJE
A broader answer:
The PIVOT clause is only a syntactic sugar that simplifies a general "classic" pivot query which is using aggregate function and GROUP BY clause, like this:
SELECT id,
max( CASE WHEN some_column = 'X' THEN value END ) As x,
max( CASE WHEN some_column = 'Y' THEN value END ) As y,
max( CASE WHEN some_column = 'Z' THEN value END ) As z
FROM table11
GROUP BY id
More on PIVOT queries you can find on the net, there is a lot of excelent explanations how the pivot query works.
The above pivot query, written in "standard" SQL, is equivalent to this Oracle's query:
SELECT *
FROM table11
PIVOT (
max(value) FOR some_column IN ( 'X', 'Y', 'Z' )
)
These PIVOT queries transform records like this:
ID SOME_COLUMN VALUE
---------- ----------- ----------
1 X 10
1 X 15
1 Y 20
1 Z 30
into one record (for each id) like this:
ID 'X' 'Y' 'Z'
---------- ---------- ---------- ----------
1 15 20 30
Please note, that the source table contains two values for id=1 and some_column='X' -> 10 and 15. PIVOT queries uses aggregate function to support that "general" case, where there could be many source records for one record in the output. In this example 'MAX' function is used to pick greater value 15.
However PIVOT queries supports also your specific case where there is only one source record for each value in the result.
You are coming across the error as the string 'new case found' is added in the else part and the rest of the case statement deals with number . data type in the when and else clause should match.
Coming to alternate approaches you may use unpivot and get the months sales data into a single row and use analytical functions to get the 1st highest or second highest.
As others have said, the problem is that the WHEN clauses in your CASE statement are returning INTEGER values, but the ELSE is returning a character string. I completely agree with the comments regarding normalization but if you really just want to make this query work you'll need to convert the results of each WHEN clause to character, as in:
Select Saler_Id,
(
CASE
WHEN JAN_SALES>FEB_SALES AND JAN_SALES>MARCH_SALES THEN TO_CHAR(JAN_SALES)
WHEN FEB_SALES>JAN_SALES AND FEB_SALES>MARCH_SALES THEN TO_CHAR(FEB_SALES)
WHEN MARCH_SALES>JAN_SALES AND MARCH_SALES>FEB_SALES THEN TO_CHAR(MARCH_SALES)
WHEN JAN_SALES=FEB_SALES AND JAN_SALES=MARCH_SALES THEN TO_CHAR(JAN_SALES)
WHEN JAN_SALES=FEB_SALES AND JAN_SALES>MARCH_SALES THEN TO_CHAR(JAN_SALES)
WHEN JAN_SALES=MARCH_SALES AND JAN_SALES>FEB_SALES THEN TO_CHAR(JAN_SALES)
WHEN FEB_SALES=JAN_SALES AND FEB_SALES>MARCH_SALES THEN TO_CHAR(FEB_SALES)
WHEN FEB_SALES=MARCH_SALES AND FEB_SALES>JAN_SALES THEN TO_CHAR(FEB_SALES)
WHEN MARCH_SALES=JAN_SALES AND MARCH_SALES>FEB_SALES THEN TO_CHAR(MARCH_SALES)
WHEN MARCH_SALES=FEB_SALES AND MARCH_SALES>JAN_SALES THEN TO_CHAR(MARCH_SALES)
ELSE 'NEW_CASE_FOUND'
END
) FIRST_HIGHEST,
(
CASE
WHEN JAN_SALES>FEB_SALES AND FEB_SALES>MARCH_SALES THEN TO_CHAR(FEB_SALES)
WHEN FEB_SALES>JAN_SALES AND JAN_SALES>MARCH_SALES THEN TO_CHAR(JAN_SALES)
WHEN JAN_SALES>MARCH_SALES AND MARCH_SALES>FEB_SALES THEN TO_CHAR(MARCH_SALES)
ELSE 'NEW_CASE_FOUND'
END
) SECOND_HIGHEST
from
Sales_Biodata;
Best of luck.

Efficiently writing this formula in SQL server 2008

Say I have table and these are its sample rows
ChangeID Change
1 102
2 105
3 107
4 110
The change formula is
(CurrentRowChange - PreviousRowChange) / PreviousRowChange
Hence:
for 1st row it should be 0
for 2nd row it should be (105 - 102) / 102
and so on. How can I efficiently write this formula in SQL?
I know I can write a scalar function and then do a RowNumber and order By ChangeID and fetch the row number's Change value and then find the current row number - 1 and then fetch that row's Change value and do a divide.
Is there any better way to achieve this?
give this a try, assuming that CHANGEID can be deleted and it is IDENTITY.
WITH changeList
AS
(
SELECT ChangeID, [Change],
(ROW_NUMBER() OVER (ORDER BY ChangeID ASC)) -1 AS rn
FROM TableName
),
normalList
AS
(
SELECT ChangeID, [Change],
(ROW_NUMBER() OVER (ORDER BY ChangeID ASC)) AS rn
FROM TableName
)
SELECT a.ChangeID, a.[Change],
COALESCE((a.Change - b.change) / (b.change * 1.0),0) result
FROM changeList a
LEFT JOIN normalList b
ON a.rn = b.rn
SQLFiddle Demo
select cur.*
, case
when prev.ChangeId is null then 0
else 1.0 * (cur.Change - prev.Change) / prev.Change
end
from Table1 cur
left join
Table1 prev
on cur.ChangeId = prev.ChangeId + 1
SQL Fiddle example.
While the ChangeID's are sequential in the sample, I wouldn't assume that they always are. So I would do something like this:
with RankedIDs as
select ChangeID
, Change
, rank() over
(partition by ChangeID order by ChangeId) rank
where something maybe ;
select case
when r1.rank = 1 then 0
else (r1.change - r2.change) / r2.change
end SomeName
from RankedIds r1 join RankedIds r2 on r1.rank = r2.rank + 1
That's the basic idea. You might want to add divide by zero protection
select T1.ChangeID,
(1.0 * T1.Change / T2.Change) - 1 as Result
from TableName as T1
outer apply (
select top(1) T.Change
from TableName as T
where T.ChangeID < T1.ChangeID
order by T.ChangeID desc
) as T2

how to find date difference when dates are places in different rows in same table?

I have a table::
ItemID VersionNo CreatedDate
-------------------------------
1 3 7/9/2010
1 2 7/3/2010
1 1 5/3/2010
1 0 3/3/2010
2 0 4/4/2010
3 1 4/5/2010
3 0 3/4/2010
...where Version 0 means .. its a newly produced item. Here I need to find time,(time gap between two versions) and add a column as process time.
like::
ItemID VersionNo CreatedDate ProcessTime
-------------------------------------------
1 3 7/9/2010 6Days or 6*24Hrs
1 2 7/3/2010 60Days
1 1 5/3/2010 2Days
1 0 3/3/2010 ''
2 0 4/4/2010 ''
3 1 4/5/2010 31Days
3 0 3/4/2010 ''
VersionNo's are not Fixed..means with time, it could increase... How to acheive the desire result in MS Access or in SQL-Server.
Thanks in advance for all your sincere efforts.
Thanks
How about (Access):
SELECT t.ItemID,
t.VersionNo,
t.CreatedDate, (
SELECT Top 1
CreatedDate
FROM Versions v
WHERE v.ItemID=t.ItemID
And v.VersionNo<t.VersionNo
ORDER BY VersionNo DESC) AS LastDate,
DateDiff("h",[LastDate],[CreatedDate]) AS DiffHrs,
DateDiff("d",[LastDate],[CreatedDate]) AS DiffDays
FROM Versions t
Join the table with itself, like this (SQL Server):
-- create the table and your data
create table #x (ItemID int, VersionNo int, CreatedDate datetime)
go
insert into #x
select 1, 3 ,'7/9/2010'
union all select 1 ,2 ,'7/3/2010'
union all select 1 ,1 ,'5/3/2010'
union all select 1 ,0 ,'3/3/2010'
union all select 2 ,0 ,'4/4/2010'
union all select 3 ,1 ,'4/5/2010'
union all select 3 ,0 ,'3/4/2010'
go
-- The query
select v2.ItemID, v2.VersionNo, datediff(dd, v1.CreatedDate, v2.CreatedDate)
from #x v1, #x v2
where v1.ItemID = v2.ItemID and v1.VersionNo + 1 = v2.VersionNo
Here it is in Access SQL, using 3 queries, one for each step.
Query1, self-join on itemID where versionNo is smaller:
SELECT t1.itemID, t1.versionNo, t1.created, t2.versionNo AS t2Version
FROM Table1 AS t1 INNER JOIN Table1 AS t2 ON t1.itemID = t2.itemID
WHERE (t2.versionNo)<[t1].[versionNo];
Query2, limit to max of smaller versionNos:
SELECT q1.itemID, q1.versionNo, q1.created, Max(q1.t2Version) AS MaxOft2Version
FROM Query1 AS q1
GROUP BY q1.itemID, q1.versionNo, q1.created;
Query3, now do datediff:
SELECT q2.itemID, q2.versionNo, q2.created, q2.MaxOft2Version, t1.created,
DateDiff("d",[t1].[created],[Q2].[created]) AS daysdiff
FROM Query2 AS q2 INNER JOIN Table1 AS t1
ON (q2.MaxOft2Version = t1.versionNo)
AND (q2.itemID = t1.itemID);
SQL Server 2005, to handle the case where there are gaps in VersionNo.
-- Declare a query that extends your table with a new column
-- that is the sequentially numbered representation of VersionNo.
-- This could be a view, but I used a CTE. I am going to use this
-- query twice below.
WITH Sequential AS (select *,
RANK() over (partition by ItemId order by VersionNo) as SequentialVersionNo
from #T as x
)
select
v.ItemID, v.VersionNo, v.SequentialVersionNo, v.CreatedDate,
DATEDIFF(day, vPrior.CreatedDate, v.CreatedDate) as ProcessTime
from Sequential as v
left outer join Sequential as vPrior
on v.ItemID=vPrior.ItemID
and v.SequentialVersionNo = vPrior.SequentialVersionNo+1;