I can do a select top 50 percent of the table rows like this to get the first 50 percent of the records/ rows
SELECT TOP 50 PERCENT * FROM table_1
How do I select the last 50 percent without doing subquery or sorting?
Related
Two separate tables. Need the find the date that is >= in Table A based on a date in Table B. Only TransactionCode 59 in Table A should be considered.
From the example tables below my return in table B First_Tran_Date should be "01/22/2022." Table A contains over 35 million records with thousands of AccountNumber's and grows each day.
Need T-SQL to take Table B ChangeDate "01/21/2022" and find the first time Table A shows a TransactionDate on or after that date and only TransactionCode 59 counts. All other TransactionCode dates should not be evaluated for the return.
Table A:
AccountNumber TransactionDate TransactionCode
xxxx310 2/3/2022 40
xxxx310 1/19/2022 40
xxxx310 1/22/2022 59
xxxx310 1/10/2022 59
xxxx310 3/15/2022 40
xxxx310 1/25/2022 59
xxxx310 1/30/2022 40
xxxx310 1/31/2022 59
xxxx310 1/31/2022 62
xxxx310 3/8/2022 59
Table B:
Account ChangeDate First_Tran_Date COUNT_OF_DAYS
xxxx310 01/21/2022 **RESULT NEEDED** (Calculated First_Tran_Date - ChangeDate = COUNT_OF_DAYS)
I have tried the following without getting a correct result:
T-SQL example...
Created a VIEW…
WITH added_row_number AS (
SELECT
*,
ROW_NUMBER() OVER(
PARTITION BY AccountNumber
ORDER BY
TransactionDate
) AS row_number
FROM dbo.LoanTransactions
)
SELECT
*
FROM added_row_number
WHERE row_number = 1
AND TransactionDate >= '2022-03-01'
AND TransactionCode IN ('59', '61', '70', '77', '82') Used a
SELECT
from that VIEW …
SELECT
DISTINCT Account,
Prod_CD,
OldValue,
NewValue,
Acct_Open_DT,
ChangeDate,
LOSVIEW_All_Transactions_From_CORE1.TransactionDate AS First_Tran_Date,
LastTransactionDate,
CASE
WHEN Prod_CD IN ('L50', 'L51', 'L54', 'L77') THEN DATEDIFF(
DAY,
ChangeDate,
LOSVIEW_All_Transactions_From_CORE1.TransactionDate
)
ELSE DATEDIFF(DAY, Acct_Open_DT, ChangeDate)
END AS COUNT_OF_DAYS
FROM dbo.R_InsuranceCodeChanges
LEFT JOIN dbo.LOSVIEW_All_Transactions_From_CORE AS LOSVIEW_All_Transactions_From_CORE1
ON dbo.R_InsuranceCodeChanges.Account = LOSVIEW_All_Transactions_From_CORE1.AccountNumber
WHERE
dbo.R_InsuranceCodeChanges.ChangeDate >= '2022-01-01'
AND dbo.R_InsuranceCodeChanges.NewValue <> '0'
If I understand correctly you want to look up two dates, and also calculate the number of days between them?
Those dates should be the first and last time a row with TransactionCode 59 appears, for a given account, and only including records on or after a given date?
So for the data in your example, the missing date should be 2022-01-22? Then the number of days would be 52 days?
For that I would use OUTER APPLY; which allows you to effectively run a query once for each input row...
SELECT
*,
DATEDIFF(DAY, table_a.MinTransactionDate, table_a.MaxTransactionDate)
FROM
table_b
OUTER APPLY
(
SELECT
MIN(TransactionDate) AS MinTransactionDate,
MAX(TransactionDate) AS MaxTransactionDate
FROM
table_a
WHERE
AccountNumber = table_b.Account
AND TransactionDate >= table_b.ChangeDate
AND TransactionCode = 59
)
AS table_a
Should be an index on tableA.TransactionDate. But I just translated your words to SQL and this is what I got:
select min(TransactionDate) as minDate
from tableA
where TransactionCode = 59
and TransactionDate >= (select max(TransactionDate) from tableB)
Hi i have 1 table Student where data is inserted row wise and i want to select only those student whose marks are more than 50% in all the subjects and if in any subject marks are less than 50% then it should not select that student in output and all records should be excluded for that student and there is no primary key
i tried below code :
Select * into #temp1 from Student where percent >=0.5 and group by Roll_Number
i am getting error :
is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
and if try like this :
Select * into #temp1 from Student where percent >=0.5
then i am getting students who has even in 1 subject more than 50% which is not required in output
Table structure is as follows
Student_Name Roll_Number Subject Marks Percent
Ashutosh 1234 English 40 40%
Ishan 1231 Maths 60 60%
Atul 1232 Maths 30 30%
Ashutosh 1234 MAths 70 70%
now in output it should only give
Ishan 1234 Maths 60 60%
You can use count() over() in order to indicate for every student if any row has < 50%, then use this as a filter criteria:
with s as (
select *,
Count(case when perc<0.5 then 1 end) over(partition by Student_Name) pc
from Students
)
select *
from s
where Percent>=0.5 and pc=0
You can get the desired result by using a subquery / cte and a window function in order to check if the student has at least 50% in all subjects:
DECLARE #Student TABLE(
Student_Name VARCHAR(20)
,Roll_Number int
,Subject VARCHAR(20)
,Marks int
,Perc DECIMAL(5,2)
)
INSERT INTO #Student VALUES
('Ashutosh',1234,'English',40,0.4)
,('Ishan',1231,'Maths',60,0.6)
,('Atul',1232,'Maths',30,0.3)
,('Ashutosh',1234,'Maths',70,0.7);
WITH cteFilter AS(
SELECT *, ROW_NUMBER() OVER (PARTITION BY Student_Name, Roll_Number ORDER BY Perc ASC, Marks ASC) rn
FROM #Student
)
SELECT *
FROM cteFilter
WHERE rn = 1
AND Perc >= 0.5
I have an SQL SELECT that left-joins several tables together, which results in an output with redundant data.
Example query:
SELECT
A.ID, B.ID
FROM A
LEFT JOIN B ON B.ParentID=A.ID
FETCH NEXT 4 ROWS ONLY
Example output:
A.ID B.ID
1 10
1 20
2 30
2 40
My problem is that I want to limit the number of rows from the A table, not from the actual output. In short, I would like to have an output like this when I ask for 4 rows:
A.ID B.ID
1 10
1 20
2 30
2 40
3 50
3 60
4 70
4 80
Any advice?
UPDATE:
Here is a fiddle that could help explaining the problem:
Fiddle
i have applied query on actual data, use sub query to get top 4 result.
declare #temp table
(aId int, bId int)
insert into #temp
values
(1,10),
(1,20),
(2,30),
(2,40),
(3,50),
(3,60),
(4,70),
(4,80),
(5,90),
(6,100)
select * from #temp
where aId in
(select distinct top 4 aId from #temp where aId > 1)
I have a need to SELECT all the rows from a table where the selected rows are greater than the datetime of the previously selected row by a given constant number of minutes. An example probably speaks best.
The following represents the table of data - we will call it myTable.
guid fkGuid myDate
------- ------- ---------------------
1 100 2013-01-10 11:00:00.0
2 100 2013-01-10 11:05:00.0
3 100 2013-01-10 11:10:00.0
4 100 2013-01-10 11:15:00.0
5 100 2013-01-10 11:20:00.0
6 100 2013-01-10 11:25:00.0
7 100 2013-01-10 11:30:00.0
8 100 2013-01-10 11:35:00.0
9 100 2013-01-10 11:40:00.0
10 100 2013-01-10 11:50:00.0
11 100 2013-01-10 11:55:00.0
What I want to do is provide a constant increment (say 10 minutes) and get back all the rows from the first that are 10 minutes or more from the previous row. So, with 10 minutes the result set should look like this:
guid myDate
------- ---------------------
1 2013-01-10 11:00:00.0
3 2013-01-10 11:10:00.0
5 2013-01-10 11:20:00.0
7 2013-01-10 11:30:00.0
9 2013-01-10 11:40:00.0
11 2013-01-10 11:55:00.0
The constant is passed in as a variable so it could be anything. Let's say it was 23 minutes, then the result set should look like this:
guid myDate
------- ---------------------
1 2013-01-10 11:00:00.0
6 2013-01-10 11:25:00.0
10 2013-01-10 11:50:00.0
The last example shows that I start at row 0's time (11:00:00) add 23 minutes and get the next >= row which is 11:25:00, add 23 minutes to the new row's time and then get the next (11:50:00) and so on.
I have tried doing this with a CTE but although I can quite easily get back all my times or none of them, I can't seem to figure how to get the rows I need. My current test code using 23 minutes hard coded into the WHERE clause:
WITH myCTE AS
(
SELECT guid,
myDate,
ROW_NUMBER() OVER (PARTITION BY guid ORDER BY myDate ASC) AS rowNum
FROM myTable
WHERE fkGuid = 100
)
SELECT currentRow.guid, currentRow.myDate
FROM myCTE AS currentRow
LEFT OUTER JOIN
myCTE AS previousRow
ON currentRow.guid = previousRow.guid
AND currentRow.rowNum = previousRow.rowNum + 1
WHERE
currentRow.myDate > DATEADD(minute, 23, previousRow.myDate)
ORDER BY
currentRow.myDate ASC
This returns nothing. If I omit the WHERE clause I get all rows back (obviously because I'm not filtering).
What am I missing?
Any and all help would be very much appreciated as it always is!
#gilly3, hardly SQL voodoo
WITH CTE
AS
(
SELECT TOP 1
guid
,fkGuid
,myDate
,ROW_NUMBER() OVER (ORDER BY myDate) RowNum
FROM MyTable
UNION ALL
SELECT mt.guid
,mt.fkGuid
,mt.myDate
,ROW_NUMBER() OVER (ORDER BY mt.myDate)
FROM MyTable mt
INNER JOIN
CTE ON mt.myDate>=DATEADD(minute,23,CTE.myDate)
WHERE RowNum=1
)
SELECT guid
,fkGuid
,myDate
FROM CTE
WHERE RowNum=1
The SQL Fiddle is here
First, your join will never return any rows, regardless of the where clause. Guid and rowNum are both unique keys per row, so if the guid is the same, so will be the rowNum. You can see that the join always fails by adding a field from previousRow to your select list and running your query without the where clause.
Next, joining on rowNum + 1 prevents skipping rows. You will only select adjacent rows that satisfy the date filter.
There may be some SQL voodoo with recursive queries that will make this work, but there will be a huge performance hit. Filter the data in your application code. Eg, in C#:
List<DataRow> FilterByInterval(IEnumerable<DataRow> rows, string dateColumn, int minutes)
{
List<DataRow> filteredRows = new List<DataRow>();
DateTime lastDate = DateTime.MinValue;
foreach (DataRow row in rows)
{
DateTime dt = row.Field<DateTime>(dateColumn);
TimeSpan diff = dt - lastDate;
if (diff.TotalMinutes >= minutes)
{
filteredRows.Add(row);
lastDate = dt;
}
}
return rows;
}
I know that I can select elements between two rows like:
SELECT * FROM table WHERE id BETWEEN 20 AND 30
Can someone tell me how can I select using one query between 4 rows like between 20 and 30 and between 40 and 50?
SELECT * FROM table WHERE id BETWEEN 20 AND 30 OR id BETWEEN 40 and 50