T-sql get min and max value for each day - sql

I am trying to write a query where for each day I get the minimum and maximum price for each item from price details table.
In price details table prices are set multiple times a day so there are many records for the same date. So I want a table where there is one row for each date and then join that table to the same table so for each distinct date I want the minimum and maximum value.
USE [a_trading_system]
GO
/****** Object: Table [dbo].[price_details] Script Date: 07/01/2012 17:28:31 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[price_details](
[price_id] [int] IDENTITY(1,1) NOT NULL,
[exch_ticker] [varchar](8) NOT NULL,
[price_set_date] [datetime] NOT NULL,
[buy_price] [decimal](7, 2) NOT NULL,
[sell_price] [decimal](7, 2) NOT NULL,
CONSTRAINT [PK_price_detail] PRIMARY KEY CLUSTERED
(
[price_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[price_details] WITH CHECK ADD CONSTRAINT [FK_price_details_Contract] FOREIGN KEY([exch_ticker])
REFERENCES [dbo].[Contract] ([exch_ticker])
GO
ALTER TABLE [dbo].[price_details] CHECK CONSTRAINT [FK_price_details_Contract]
GO
SQL Query
select distinct
substring(convert(varchar(12),p1.price_set_date), 0, 12),
p2.exch_ticker,
(select MIN(buy_price) from price_details ),
(select MAX(buy_price) from price_details )
from price_details as p1
left join price_details as p2 on p2.exch_ticker = p1.exch_ticker
where p1.exch_ticker = p2.exch_ticker
group by p1.price_set_date, p2.exch_ticker
Summary
Table has many prices set on the same day. Want min and max values for each day for each exch ticker.
Thanks

A simple group by should work:
select cast(price_set_date as date) as [Date]
, exch_ticker
, min(buy_price) as MinPrice
, max(buy_price) as MaxPrice
from price_details as p
group by
exch_ticker
, cast(price_set_date as date)
Not sure why your example query is using a self join. If there's a good reason please add an explanation to your question.

Related

sql - Add calculated column to existing table of max date grouped by id

I have an existing table I have created and I would like to alter my table by creating a new calculated column which will obtain the max date by ID.
Multiple times a day, new data will flow into the table from a form input (web app). Every time the ID and datestamp are entered into the database, I would like the calculated column to update.
I created my table to interact with Powerapps web form:
CREATE TABLE [dbo].[test_table2](
[Id] [int] IDENTITY(1,1) NOT NULL,
[datestamp] [date] NULL,
CONSTRAINT [tableId] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
I have tried to create a column with something like this.....
SELECT id, datestamp FROM (
SELECT id, datestamp,
RANK() OVER (PARTITION BY id ORDER BY datestamp DESC) max_date_id
FROM test_table2
) where max_date_id = 1
I ultimately would like to alter the table so it updates automatically, but this code is just a query and also does not work. How can I achieve this?

Why calculated fields using scalar functions are slow

I have below table:
CREATE TABLE [dbo].[Client](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](150) NOT NULL,
[InternalSiteId] AS (isnull(CONVERT([int],[dbo].[GetCurrentTemporalValue]([Id],'Client_InternalSite')),(0))),
[BudgetingStatusId] AS (isnull(CONVERT([int],[dbo].[GetCurrentTemporalValue]([Id],'Client_BudgetingStatus')),(1))),
[BusinessUnitId] AS (isnull(CONVERT([int],[dbo].[GetCurrentTemporalValue]([Id],'Client_BusinessUnit')),(0))),
CONSTRAINT [PK_dbo.Client] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
which has 3 calculated fields using scalar function:
CREATE FUNCTION [dbo].[GetCurrentTemporalValue]
(
#clientId INT,
#temporalType NVARCHAR(128)
)
RETURNS NVARCHAR(255)
AS
BEGIN
DECLARE #retVal INT
DECLARE #at DATETIME
SET #at = GETUTCDATE()
SELECT #retVal = CAST(Value AS INT) FROM dbo.Temporal
WHERE 1 = 1
AND ClientId = #clientId
AND TemporalType = #temporalType
AND ( ValidFrom <= #at OR ValidFrom IS NULL )
AND ( ValidTo >= #at OR ValidTo IS NULL)
RETURN #retVal
END
GO
and this function uses below table:
CREATE TABLE [dbo].[Temporal](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ClientId] [int] NOT NULL,
[Value] [nvarchar](255) NULL,
[ValidFrom] [datetime2](7) NULL,
[ValidTo] [datetime2](7) NULL,
[TemporalType] [nvarchar](128) NOT NULL,
CONSTRAINT [PK_dbo.Temporal] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
The Client table has around 2500 records but the select * from [Client] takes about 14 seconds!
When I comment these 3 calculated fields, it gets back to normal value below one second. So the scalar function seems to cause the issue.
I tried to make condition easier ( left only 1 = 1 ) but it didn't change the speed.
What I want to achieve is:
historical values (kept in Temporal table)
current value to be pointed out by Client.InternalSiteId, Client.BudgetingStatusId and Client.BusinessUnitId
I cannot just simply make InternalSiteId a regular int fields with foreign key to, let say, InternalSiteHistoryTable as there are ValidFrom and ValidTo fields pointing out the period in which given value is valid and current and e.g. ValidFrom can be set to future value. That's why I need such calculations in function to find out current value.
What should I do / change to achieve above goals, but keep the reasonable fetching data speed?
Use a view to join to your Temporal table instead of embedding function calls
-- Table without those functions to slow things down
CREATE TABLE [dbo].[ClientName](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](150) NOT NULL,
CONSTRAINT [PK_dbo.Client] PRIMARY KEY CLUSTERED
(
[Id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF)
ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
--instead, use a view to do the same thing
CREATE VIEW [dbo].[Client] as
SELECT
ClientName.Id,
ClientName.Name,
isnull(CONVERT([int], Client_InternalSite.Value), 0) AS InternalSiteId,
isnull(CONVERT([int], Client_BudgetingStatus.Value, 1) AS BudgetingStatusId,
isnull(CONVERT([int], Client_BusinessUnit.Value, 0) AS BusinessUnitId
FROM ClientName INNER JOIN
( SELECT Value, ClientId
FROM Temporal
WHERE TemporalType='Client_InternalSite'
AND ( ValidFrom <= GETUTCDATE() OR ValidFrom IS NULL )
AND ( ValidTo >= GETUTCDATE() OR ValidTo IS NULL)
) AS Client_InternalSite ON ClientName.ID = Client_InternalSite.ClientID
INNER JOIN
( SELECT Value, ClientId
FROM Temporal
WHERE TemporalType='Client_BudgetingStatus'
AND ( ValidFrom <= GETUTCDATE() OR ValidFrom IS NULL )
AND ( ValidTo >= GETUTCDATE() OR ValidTo IS NULL)
) AS Client_BudgetingStatus ON ClientName.ID = Client_BudgetingStatus.ClientID
INNER JOIN
( SELECT Value, ClientId
FROM Temporal
WHERE TemporalType='Client_BusinessUnit'
AND ( ValidFrom <= GETUTCDATE() OR ValidFrom IS NULL )
AND ( ValidTo >= GETUTCDATE() OR ValidTo IS NULL)
) AS Client_BusinessUnit ON ClientName.ID = Client_BusinessUnit.ClientID
GO
I can't test this against your DB, so I don't know about your indexes on your Temporal table (ID, ValidFrom, ValidTo columns), but typically a VIEW like this is going to run quicker because the tables are queried only once.

Issues in SYSTEM_VERSIONING OFF - Insert record failed in SQL Server

I'm having a System Versioned Temporal Table namely [dbo].[Contact], for development purpose I tried to seed some old dated data. The table contains GENERATED ALWAYS column.
This question is a Child question of my exiting question Seed data with old dates in Temporal Table - SQL Server
Original Table Schema:
CREATE TABLE [dbo].[Contact](
[ContactID] [uniqueidentifier] NOT NULL,
[ContactNumber] [nvarchar](50) NOT NULL,
[SysStartTime] [datetime2](0) GENERATED ALWAYS AS ROW START NOT NULL,
[SysEndTime] [datetime2](0) GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_Contact] PRIMARY KEY NONCLUSTERED
(
[ContactID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime])
) ON [PRIMARY]
WITH
(
SYSTEM_VERSIONING = ON (HISTORY_TABLE = [dbo].[ContactHistory] , DATA_CONSISTENCY_CHECK = ON )
)
I tried to switch it OFF the SYSTEM_VERSIONING, by using the following SQL Code
ALTER TABLE dbo.Contact SET (SYSTEM_VERSIONING = OFF);
Now the Table becomes like a normal table
Now I tried to Insert a record in [dbo].[Contact]
INSERT INTO dbo.Contact
(
ContactID,
ContactNumber,
SysStartTime,
SysEndTime
)
VALUES
(
NEWID(), -- ContactID - uniqueidentifier
N'1234567890', -- ContactNumber - nvarchar
'2014-09-13 00:00:00', -- SysStartTime - datetime2
'9999-12-31 23:59:59' -- SysEndTime - datetime2
)
But Still I'm getting the same Error
Msg 13536, Level 16, State 1, Line 20 Cannot insert an explicit value
into a GENERATED ALWAYS column in table 'DevDB.dbo.Contact'. Use
INSERT with a column list to exclude the GENERATED ALWAYS column, or
insert a DEFAULT into GENERATED ALWAYS column.

T-SQL get price between two dates

I am making a small database trading system and I have a problem with duplication which I am unsure how to solve. Basically I have a table with prices with the datetime which that price was set and I also have a table with the time a trade was made. I want to get the correct price based on trade datetime.
USE [a_trading_system]
GO
/****** Object: Table [dbo].[Trade] Script Date: 06/30/2012 14:49:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Trade](
[trade_id] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[trade_volume] [int] NOT NULL,
[trade_action] [varchar](5) NOT NULL,
[trade_date] [datetime] NOT NULL,
[timestap] [timestamp] NOT NULL,
[trader_id] [int] NOT NULL,
[exch_ticker] [varchar](8) NOT NULL,
CONSTRAINT [PK_Trades] PRIMARY KEY CLUSTERED
(
[trade_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Trade] WITH CHECK ADD CONSTRAINT [FK_Trade_Contract] FOREIGN KEY([exch_ticker])
REFERENCES [dbo].[Contract] ([exch_ticker])
GO
ALTER TABLE [dbo].[Trade] CHECK CONSTRAINT [FK_Trade_Contract]
GO
ALTER TABLE [dbo].[Trade] WITH CHECK ADD CONSTRAINT [FK_Trade_Trader] FOREIGN KEY([trader_id])
REFERENCES [dbo].[Trader] ([trader_id])
GO
ALTER TABLE [dbo].[Trade] CHECK CONSTRAINT [FK_Trade_Trader]
GO
ALTER TABLE [dbo].[Trade] ADD CONSTRAINT [DF_Trades_trade_id] DEFAULT (newid()) FOR [trade_id]
GO
USE [a_trading_system]
GO
/****** Object: Table [dbo].[Contract] Script Date: 06/30/2012 14:56:19 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Contract](
[exch_ticker] [varchar](8) NOT NULL,
[exch_name] [varchar](50) NULL,
[portfolio_id] [varchar](8) NOT NULL,
[region_cd] [varchar](5) NULL,
CONSTRAINT [PK_Contract] PRIMARY KEY CLUSTERED
(
[exch_ticker] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Contract] WITH CHECK ADD CONSTRAINT [FK_Contract_portfolio] FOREIGN KEY([portfolio_id])
REFERENCES [dbo].[portfolio] ([portfolio_id])
GO
ALTER TABLE [dbo].[Contract] CHECK CONSTRAINT [FK_Contract_portfolio]
GO
ALTER TABLE [dbo].[Contract] WITH CHECK ADD CONSTRAINT [FK_Contract_region] FOREIGN KEY([region_cd])
REFERENCES [dbo].[Region] ([region_cd])
GO
ALTER TABLE [dbo].[Contract] CHECK CONSTRAINT [FK_Contract_region]
GO
USE [a_trading_system]
GO
/****** Object: Table [dbo].[price_details] Script Date: 06/30/2012 14:58:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[price_details](
[price_id] [int] IDENTITY(1,1) NOT NULL,
[exch_ticker] [varchar](8) NOT NULL,
[price_set_date] [datetime] NOT NULL,
[buy_price] [decimal](7, 2) NOT NULL,
[sell_price] [decimal](7, 2) NOT NULL,
CONSTRAINT [PK_price_detail] PRIMARY KEY CLUSTERED
(
[price_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[price_details] WITH CHECK ADD CONSTRAINT [FK_price_details_Contract] FOREIGN KEY([exch_ticker])
REFERENCES [dbo].[Contract] ([exch_ticker])
GO
ALTER TABLE [dbo].[price_details] CHECK CONSTRAINT [FK_price_details_Contract]
GO
View
USE [a_trading_system]
GO
/****** Object: View [dbo].[V_all_uk] Script Date: 06/30/2012 14:39:18 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER VIEW [dbo].[V_all_uk]
AS
SELECT distinct
co.exch_ticker,
--co.region_cd,
po.portfolio_type,
r.region_name,
r.currency,
t.trade_id,
t.trade_volume,
t.trade_action,
t.trade_date,
pr.buy_price,
--(select distinct pr.buy_price from price_details pr
--where pr.price_set_date <= t.trade_date or pr.price_set_date >= t.trade_date) as price_details,
--MIN(t.trade_date) as trade_date,
--pr.buy_price,
--pr.sell_price,
--pr.price_set_date, --This is the cause of duplication
--pr.price_set_time,case when t.trade_date IS NOT NULL then
--case
--when t.trade_action = 'Buy' then
--t.trade_volume * max(pr.buy_price)
--else
--case when trade_action = 'Sell' then
--t.trade_volume * max(pr.sell_price)
--end
--end as 'trade_value' ,
tr.trader_name,
tr.trader_address,
tr.phone
FROM dbo.Contract as co
INNER JOIN dbo.Portfolio as po ON co.portfolio_id = po.portfolio_id
INNER JOIN dbo.region as r ON co.region_cd = r.region_cd
INNER JOIN dbo.Trade as t ON co.exch_ticker = t.exch_ticker
INNER JOIN dbo.trader as tr ON t.trader_id = tr.trader_id
inner join dbo.price_details as pr on pr.exch_ticker = t.exch_ticker
where r.region_cd = 'UK'
--group by
--co.exch_ticker,
--co.region_cd,
--po.portfolio_type,
--r.region_name,
--r.currency,
--t.trade_id,
--t.trade_volume,
--t.trade_action,
--pr.buy_price,
--pr.sell_price,
--tr.trader_name,
--tr.trader_address,
--tr.phone
GO
These are the three main tables if you need to see data as well just say because I don't usually post SQL questions on this website.
Explanation
If a price is set at 12:20 and the price is 100 then at 12:40 the price is 80. These are two date ranges. So if I buy at 12:30 then I am buying at the price of 100 because that is last price. I am also doing my joins in a view so I can see all data. I will post that now.
Thanks
To get the latest price prior to a particular trade date:
select buy_price, sell_price
from price_details
where exch_ticker = #exch_ticker and price_set_date =
( select max( price_set_date )
from price_details
where exch_ticker = #exch_ticker and price_set_date <= #trade_date )
You may want to add an index on exch_ticker/trade_date(desc) to price_details.
The following assumes SQL Server 2005 or later version.
The idea is first to join Trade and price_details filtering out prices whose times are greater than the corresponding trades' times:
SELECT ...
FROM dbo.Trade t
INNER JOIN dbo.price_details pr ON pr.exch_ticker = t.exch_ticker
The above will get you a row set where every trade has got all prices up to the time of the trade. Now just rank the prices and get the latest one:
WITH trade_prices AS (
SELECT
t.*, -- actually you might want to review the list
pr.price_set_date, -- of columns being pulled from the two tables
pr.buy_price,
pr.sell_price,
rnk = ROW_NUMBER() OVER (PARTITION BY t.trade_id ORDER BY pr.price_set_date DESC)
FROM dbo.Trade t
INNER JOIN dbo.price_details pr ON pr.exch_ticker = t.exch_ticker
)
SELECT *
FROM trade_prices
WHERE rnk = 1
To incorporate this into your view, you will only need to:
1) add the trade_prices CTE,
2) replace two joins, to Trade and to price_details, with a join to trade_prices,
3) add the trade_prices.rnk = 1 condition to the WHERE clause.
Of course, the trader table will now be joined to trade_prices instead of to Trade. And you will also need to change the table aliases pr and t in the view's select list to the one you choose to assign to trade_prices.

Select count from another table to each row in result rows

Here are the tables:
CREATE TABLE [dbo].[Classes](
[ClassId] [int] NOT NULL,
[ClassName] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Classes] PRIMARY KEY CLUSTERED
(
[ClassId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Students](
[StudentId] [int] NOT NULL,
[ClassId] [int] NOT NULL,
CONSTRAINT [PK_Students] PRIMARY KEY CLUSTERED
(
[StudentId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Students] WITH CHECK ADD CONSTRAINT [FK_Students_Classes] FOREIGN KEY([ClassId])
REFERENCES [dbo].[Classes] ([ClassId])
GO
ALTER TABLE [dbo].[Students] CHECK CONSTRAINT [FK_Students_Classes]
GO
I want to get list of class, and each class - the number of student which belong to each class.
How can I do this?
You need to do this -
SELECT C.ClassId, C.ClassName, count(S.StudentId) AS studentCount
FROM CLASSES C LEFT JOIN STUDENTS S ON (C.ClassId=S.ClassId)
GROUP BY C.ClassId, C.ClassName
You mean something like this?
SELECT C.[ClassName], COUNT(*) AS 'Number of Students'
FROM [dbo].[Classes] AS C
INNER JOIN [dbo].[Students] AS S ON S.[ClassId] = C.[ClassId]
GROUP BY C.[ClassName]
Without having to add the group by clause, you can do the following:
Create a function to get the students count:
go
CREATE FUNCTION [dbo].GetStudentsCountByClass(#classId int) RETURNS INT
AS BEGIN
declare #count as int
select #count = count(*) from STUDENTS
where ClassId = #classId
RETURN #count
END
then use it in your select statement
SELECT * , dbo.GetStudentsCountByClass(ClassId) AS StudentsCount
FROM Classes
select c.ClassId,C.ClassName,COUNT(*) [Number of students]
from Classes C,Students S
where c.ClassId=S.ClassId
group by C.ClassId,C.ClassName
SELECT class.ClassId, count(student .StudentId) AS studentCount
FROM dbo.CLASSES class LEFT JOIN dbo.STUDENTS student ON (class.ClassId=student.ClassId)
GROUP BY class.ClassId