Select first 10 rows of a column in a table - sql

Can you please let me know how to select first 10 rows for every cusip in the below table
SELECT [dt_dim_id],
[cusip_num],
sr.[product_dim_id],
[acct_dim_id],
[product_name]
FROM [csr_prod].[dbo].[stock_rec] AS sr,
[csr_prod].[dbo].[product_dim] AS pd
WHERE sr.product_dim_id = pd.product_dim_id
AND dt_dim_id = 20180927
ORDER BY dt_dim_id,
product_dim_id,
acct_dim_id;

Use ROW_NUMBER() with a partition over your groups and order by whatever you need, then filter for the first 10 rows:
;WITH paging AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY cusip_num
ORDER BY dt_dim_id, product_dim_id, acct_dim_id) n,
[dt_dim_id],
[cusip_num],
sr.[product_dim_id],
[acct_dim_id],
[product_name]
FROM [csr_prod].[dbo].[stock_rec] AS sr,
[csr_prod].[dbo].[product_dim] AS pd
WHERE sr.product_dim_id = pd.product_dim_id
AND dt_dim_id = 20180927
)
SELECT * FROM paging WHERE n <= 10

Use apply:
select . . .
from [csr_prod].[dbo].[stock_rec] sr cross apply
(select top (10) pd.*
from [csr_prod].[dbo].[product_dim] pd =
where sr.product_dim_id = pd.product_dim_id and dt_dim_id = 20180927
order by ? -- whatever column defines the first 10 records
) p

Related

SQL: Showing column name of calculated result

So I am calculating the max of a series of column and I was wondering what the best way to show the name of the result column.
Example Table:
hour1 hour2 hour3
16 10 5
My query looks like this:
(SELECT Max(v)
FROM (VALUES (hour1) , (hour2) , (hour3))
AS VALUE (v)) AS PEAK_VALUE
Note this is in another query.
Desired output:
PEAK_VALUE PEAK_HOUR
16 hour1
I would also like to do further calculations on the PEAK_VALUE column as well. For example dividing it by 2 for this output:
PEAK_VALUE HALF_VALUE PEAK_HOUR
16 8 hour1
You almost got it though you have some issue with your query syntax. You need to add the column name to the un-pivot. After that user row_number() to find the max value
SELECT PEAK_VALUE = v,
HALF_VALUE = v / 2,
PEAK_HOUR = c
FROM
(
SELECT *, rn = row_number() over (order by v desc)
FROM example
CROSS APPLY
(
VALUES ('hour1', hour1) , ('hour2', hour2) , ('hour3', hour3)
) AS VALUE (c, v)
) v
WHERE rn = 1
dbfiddle
Just another option sans row_number()
Example
Select top 1
Peak_Value = Hour
,Half_Value = Hour/2
,Peak_Hour = Item
From YourTable A
Cross Apply (values ('hour1',hour1)
,('hour2',hour2)
,('hour3',hour3)
) B (Item,Hour)
Order by Hour Desc
Results
Peak_Value Half_Value Peak_Hour
16 8 hour1
Another approach using UNPIVOT.
DECLARE #table table(hour1 int, hour2 int, hour3 int)
insert into #table
values(16,10,5)
SELECT top 1 max(val) as peak_value, max(val) /2 as Half_Value, [hour]
FROM #table
unpivot (val for [hour] in ([hour1],[hour2],[hour3])) as t
group by [hour]
order by max(val) desc
peak_value
Half_Value
hour
16
8
hour1
You can shove a whole unpivoting query inside a CROSS APPLY, and then do further calculations:
SELECT t.*, v.*
FROM yourTable t
CROSS APPLY (
SELECT TOP (1)
v.PEAK_VALUE,
HALF_VALUE = v.PEAK_VALUE / 2,
v.PEAK_HOUR,
FROM (VALUES
('hour1', hour1),
('hour2', hour2),
('hour3', hour3)
) AS v(PEAK_HOUR, PEAK_VALUE)
ORDER BY PEAK_VALUE DESC
) AS v
This is slightly different from the other answers, in that it will calculate the subquery per row

Maximum number of statements within CTE

WITH abidAccount AS
(
SELECT
[ID], AzureBlobInsertDate = MAX(AzureBlobInsertDate)
FROM
[dba].[CurAccounts]
GROUP BY
ID
),
recentAccount AS
(
SELECT ca.*
FROM [dba].[CurAccounts] ca
JOIN abidAccount aa ON aa.[id] = ca.[ID]
AND aa.azureblobInsertDate = ca.azureBlobInsertDate
),
abidDevice AS
(
SELECT deviceID, azureBlobInsertDate = MAX(azureBlobInsertDate)
FROM [dba].[CurrentDevices]
GROUP BY DeviceID
),
recentDevice AS
(
SELECT cd.*
FROM [RZRExploreLayer3].[CurrentDevices] cd
JOIN abidDevice ad ON ad.DeviceID = cd.DeviceID
AND ad.azureBlobInsertDate = cd.azureblobinsertdate
)
SELECT
rd.deviceId,
rd.[DeviceReturned],
rd.accountNumber,
rd.[DeviceFormat],
rd.[DeviceLabel],
MAX(ra.azureBlobInsertDate) AS AzureBlobInsertDate
FROM
recentAccount ra
JOIN
recentDevice rd ON ra.[id] = rd.accountNumber
WHERE
rd.deviceReturned NOT LIKE 'Null'
GROUP BY
rd.deviceId, rd.[DeviceReturned], rd.accountNumber,
rd.[DeviceFormat], rd.[DeviceLabel]
/* deviceID, rd.[DeviceReturned], rd.accountNumber, ra.azureBlobInsertDate */
HAVING
COUNT(1) > 1
How do I combine multiple CTE into one query?
My query is attempting to determine if there are duplicate records and if so only keep the max(AzureBlobInsertDate) record and remove other duplicates. then combine all the results from the CurAccounts & Devices tables.
Any assistance you can offer is greatly appreciated.

Need help creating SQL query from example of data

I have a database table below.
And I want to get list of all DBKey that have: at least one entry with Staled=1, and the last entry is Staled=0
The list should not contain DBKey that has only Staled=0 OR Staled=1.
In this example, the list would be: DBKey=2 and DBKey=3
I think this should do the trick:
SELECT DISTINCT T.DBKey
FROM TABLE T
WHERE
-- checks that the DBKey has at least one entry with Staled = 1
EXISTS (
SELECT DISTINCT Staled
FROM TABLE
WHERE DBKey = T.DBKey
AND Staled = 1
)
-- checks that the last Staled entry for this DBKey is 0
AND EXISTS (
SELECT DISTINCT Staled
FROM TABLE
WHERE DBKey = T.DBKey
AND Staled = 0
AND EntryDateTime = (
SELECT MAX(EntryDateTime)
FROM TABLE
WHERE DBKey = T.DBKey
)
)
Here is a working SQLFiddle of the query, using your sample data.
The idea is to use EXISTS to look for those individual conditions that you've described. I've added comments to my code to explain what each does.
Should be done with a simple JOIN... Starting FIRST with any 1 qualifiers, joined to itself by same key AND 0 staled qualifier AND the 0 record has a higher date. Ensure you have an index on ( DBKey, Staled, EntryDateTime )
SELECT
YT.DBKey,
MAX( YT.EntryDateTime ) as MaxStaled1,
MAX( YT2.EntryDateTime ) as MaxStaled0
from
YourTable YT
JOIN YourTable YT2
ON YT.DBKey = YT2.DBKey
AND YT2.Staled = 0
AND YT.EntryDateTime < YT2.EntryDateTime
where
YT.Staled = 1
group by
YT.DBKey
having
MAX( YT.EntryDateTime ) < MAX( YT2.EntryDateTime )
Maybe this:
With X as
(
Select Row_Number() Over (Partition By DBKey Order By EntryDateTime Desc) RN, DBKey, Staled
From table
)
Select *
From X
Where rn = 1 and staled = 0 and
Exists (select 1 from x x2 where x2.dbkey = x.dbkey and Staled = 1)

SQL ROW_NUMBER with INNER JOIN

I need to use ROW_NUMBER() in the following Query to return rows 5 to 10 of the result. Can anyone please show me what I need to do? I've been trying to no avail. If anyone can help I'd really appreciate it.
SELECT *
FROM villa_data
INNER JOIN villa_prices
ON villa_prices.starRating = villa_data.starRating
WHERE villa_data.capacity >= 3
AND villa_data.bedrooms >= 1
AND villa_prices.period = 'lowSeason'
ORDER BY villa_prices.price,
villa_data.bedrooms,
villa_data.capacity
You need to stick it in a table expression to filter on ROW_NUMBER. You won't be able to use * as it will complain about the column name starRating appearing more than once so will need to list out the required columns explicitly. This is better practice anyway.
WITH CTE AS
(
SELECT /*TODO: List column names*/
ROW_NUMBER()
OVER (ORDER BY villa_prices.price,
villa_data.bedrooms,
villa_data.capacity) AS RN
FROM villa_data
INNER JOIN villa_prices
ON villa_prices.starRating = villa_data.starRating
WHERE villa_data.capacity >= 3
AND villa_data.bedrooms >= 1
AND villa_prices.period = 'lowSeason'
)
SELECT /*TODO: List column names*/
FROM CTE
WHERE RN BETWEEN 5 AND 10
ORDER BY RN
You can use a with clause. Please try the following
WITH t AS
(
SELECT villa_data.starRating,
villa_data.capacity,
villa_data.bedrooms,
villa_prices.period,
villa_prices.price,
ROW_NUMBER() OVER (ORDER BY villa_prices.price,
villa_data.bedrooms,
villa_data.capacity ) AS 'RowNumber'
FROM villa_data
INNER JOIN villa_prices
ON villa_prices.starRating = villa_data.starRating
WHERE villa_data.capacity >= 3
AND villa_data.bedrooms >= 1
AND villa_prices.period = 'lowSeason'
)
SELECT *
FROM t
WHERE RowNumber BETWEEN 5 AND 10;

T-sql problem with running sum

I am trying to write T-sql script which will find "open" records for one table
Structure of data is following
Id (int PK) Ts (datetime) Art_id (int) Amount (float)
1 '2009-01-01' 1 1
2 '2009-01-05' 1 -1
3 '2009-01-10' 1 1
4 '2009-01-11' 1 -1
5 '2009-01-13' 1 1
6 '2009-01-14' 1 1
7 '2009-01-15' 2 1
8 '2009-01-17' 2 -1
9 '2009-01-18' 2 1
According to my needs I am trying to show only records after last sum for every one articles where 0 sorting by date of last running sum of zero value. So I am trying to abstract (show) records 5 and 6 for Art_id=1 and record 9 for art_id=2. I am using MSSQL2005 and my table has around 30K records with 6000 distinct values of ART_ID.
In this solution I simply want to find all the rows where there isn't a subsequent row for that Art_id where the running sum was 0. I am assuming we can use the ID as a better tiebreaker than TS, since two rows can come in with the same timestamp but they will get sequential identity values.
;WITH base AS
(
SELECT
ID, Art_id, TS, Amount,
RunningSum = Amount + COALESCE
(
(
SELECT SUM(Amount)
FROM dbo.foo
WHERE Art_id = f.Art_id
AND ID < f.ID
)
, 0
)
FROM dbo.[table name] AS f
)
SELECT ID, Art_id, TS, Amount
FROM base AS b1
WHERE NOT EXISTS
(
SELECT 1
FROM base AS b2
WHERE Art_id = b1.Art_id
AND ID >= b1.ID
AND RunningSum = 0
)
ORDER BY ID;
Complete working query:
SELECT
*
FROM TABLE_NAME E
JOIN
(SELECT
C.ART_ID,
MAX(TS) MAX_TS
FROM
(SELECT
ART_ID,
TS,
COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
FROM TABLE_NAME A) C
WHERE C.ROW_SUM = 0
GROUP BY C.ART_ID) D
ON
(D.ART_ID = E.ART_ID) AND
(E.TS >= D.MAX_TS)
First we calculate running sums for every row:
SELECT
ART_ID,
TS,
COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
FROM TABLE_NAME A
Then we look for last article with 0:
SELECT
C.ART_ID,
MAX(TS) MAX_TS
FROM
(SELECT
ART_ID,
TS,
COALESCE((SELECT SUM(AMOUNT) FROM TABLE_NAME B WHERE (B.Art_id = A.Art_id) AND (B.Ts < A.Ts)),0) ROW_SUM
FROM TABLE_NAME A) C
WHERE C.ROW_SUM = 0
GROUP BY C.ART_ID
You can find all rows where the running sum is zero with:
select cur.id, cur.art_id
from #articles cur
left join #articles prev
on prev.art_id = cur.art_id
and prev.id <= cur.id
group by cur.id, cur.art_id
having sum(prev.amount) = 0
Then you can query all rows that come after the rows with a zero running sum:
select a.*
from #articles a
left join (
select cur.id, cur.art_id, running = sum(prev.amount)
from #articles cur
left join #articles prev
on prev.art_id = cur.art_id
and prev.ts <= cur.ts
group by cur.id, cur.art_id
having sum(prev.amount) = 0
) later_zero_running on
a.art_id = later_zero_running.art_id
and a.id <= later_zero_running.id
where later_zero_running.id is null
The LEFT JOIN in combination with the WHERE says: there can not be a row after this row, where the running sum is zero.