Conditional Selection of Rows Using TSQL SQL Server (2008 R2) - sql
I've been staring at this for hours and hours and can't come up with an "elegant" set-based way of getting the result set I need...
Here's my sample data (my real data could be 1,000,000+ rows)...
DECLARE #t AS TABLE (ID int,ID1 nvarchar(15),[DATE] date,PERIOD int,[TYPE] nchar(1));
INSERT INTO #t (ID,ID1,[DATE],PERIOD,[TYPE])
VALUES
(1,N'NUM1','2016-01-01',1,N'B'),
(2,N'NUM1','2016-01-01',2,N'A'),
(3,N'NUM1','2016-01-01',3,N'A'),
(4,N'NUM1','2016-01-01',4,N'B'),
(5,N'NUM1','2016-01-01',4,N'A'),
(6,N'NUM1','2016-01-01',5,N'A'),
(7,N'NUM1','2016-01-02',1,N'A'),
(8,N'NUM1','2016-01-02',2,N'A'),
(9,N'NUM1','2016-01-02',3,N'A'),
(10,N'NUM1','2016-01-02',4,N'A'),
(11,N'NUM1','2016-01-02',5,N'A'),
(12,N'NUM2','2016-01-01',1,N'A'),
(13,N'NUM2','2016-01-01',1,N'B'),
(14,N'NUM2','2016-01-01',2,N'A'),
(15,N'NUM2','2016-01-01',3,N'A'),
(16,N'NUM2','2016-01-01',4,N'B'),
(17,N'NUM2','2016-01-01',4,N'A'),
(18,N'NUM2','2016-01-01',5,N'A'),
(19,N'NUM2','2016-01-02',1,N'A'),
(20,N'NUM2','2016-01-02',2,N'B'),
(21,N'NUM2','2016-01-02',3,N'A'),
(22,N'NUM2','2016-01-02',4,N'A'),
(23,N'NUM2','2016-01-02',4,N'B'),
(24,N'NUM2','2016-01-02',5,N'A');
Here is the result set I'm trying to get...
1,'NUM1','2016-01-01',1,'B'
2,'NUM1','2016-01-01',2,'A'
3,'NUM1','2016-01-01',3,'A'
5,'NUM1','2016-01-01',4,'A'
6,'NUM1','2016-01-01',5,'A'
7,'NUM1','2016-01-02',1,'A'
8,'NUM1','2016-01-02',2,'A'
9,'NUM1','2016-01-02',3,'A'
10,'NUM1','2016-01-02',4,'A'
11,'NUM1','2016-01-02',5,'A'
12,'NUM2','2016-01-01',1,'A'
14,'NUM2','2016-01-01',2,'A'
15,'NUM2','2016-01-01',3,'A'
17,'NUM2','2016-01-01',4,'A'
18,'NUM2','2016-01-01',5,'A'
19,'NUM2','2016-01-02',1,'A'
20,'NUM2','2016-01-02',2,'B'
21,'NUM2','2016-01-02',3,'A'
22,'NUM2','2016-01-02',4,'A'
24,'NUM2','2016-01-02',5,'A'
Simply put, each day has 5 periods. They can be of type A or B. I need to get the A types. but if there are no A types, I need to get the B types... (Sounds so simple when I write it out.., but my brain will not come up with something suitable)
Pleeeeeease put me out of my misery..
You can use ROW_NUMBER for this:
SELECT ID, ID1, [DATE], PERIOD, [TYPE]
FROM (
SELECT ID, ID1, [DATE], PERIOD, [TYPE],
ROW_NUMBER() OVER (PARTITION BY ID1, [DATE], PERIOD
ORDER BY [TYPE]) AS rn
FROM #t) AS t
WHERE t.rn = 1
Using ORDER BY [TYPE] in the OVER clause of ROW_NUMBER places 'A' records on top of 'B' records. If there are no 'A' records for a given ID1, [DATE], PERIOD then B records are assigned rn = 1.
Your desired outpout contradicts the statement that "I need to get the A types. but if there are no A types, I need to get the B types... ". Every date in the data has one or more 'A' types. By the statement, the output should include only the 'A' types. But if the statement is correct, then this should work:
Select d.[DATE], t.Id, t.ID1, t.PERIOD, t.[TYPE]
from (select distinct [date] from #t) d
left join #t t
on t.[date] = d.[date]
and t.type = case when exists
(select * from #t
where [date] = d.[Date]
and type = 'A') then 'A'
else 'B' End
I've just come up with
SELECT * FROM #t WHERE [TYPE]='A'
UNION ALL
SELECT * FROM #t t1 WHERE [TYPE]='B' AND NOT EXISTS (SELECT ID FROM #t WHERE ID1=t1.ID1 AND [TYPE]='A' AND [DATE]=t1.[DATE] AND Period=t1.Period)
ORDER BY ID;
which give's me what I need...
Related
How to use Dynamic Lag function to avoid joining a table to itself to retrieve date value
I'm currently writing code in SQL to add the column in red to the following table: The logic is the following: For every row: if flag for this row =1 then use date of this row if flag for this row =0 then find the latest row (based on date) on which flag was = 1 for the same party and return the date of that row. If no such row exists, return null I've found a way to do this by joining the table to itself but I would like to avoid doing that as the size of the table is pretty massive. What I have select b.*, a.date, from table a left join table b on a.party=b.party where a.flag =1 Someone told me I could use the lag function, the partition over function and a case when to return the value I'm after but I haven't been able to figure it out. Can someone help? Thank you so much!
try this DECLARE #tab1 TABLE(PARTY CHAR(1),DATE DATE,Flag bit) INSERT INTO #tab1 SELECT 'A','7-24-2018',1 Union ALL SELECT 'A','7-28-2018',0 Union ALL SELECT 'A','7-29-2018',0 Union ALL SELECT 'A','7-29-2018',0 Union ALL SELECT 'B','7-13-2018',1 Union ALL SELECT 'B','7-17-2018',0 Union ALL SELECT 'B','7-18-2018',0 Union ALL SELECT 'C','7-8-2018',1 Union ALL SELECT 'C','7-13-2018',0 Union ALL SELECT 'C','7-19-2018',0 Union ALL SELECT 'C','7-19-2018',0 Union ALL SELECT 'C','7-20-2018',0 select t.*, max(case when flag = 1 then date end) over (partition by PARTY order by date) as [Last Flag On Date] from #tab1 t
try this :-> select b.*, a.date, from table a left join table b on a.party=b.party where a.flag = CASE WHEN a.flag = 1 THEN a.date WHEN a.flag = 0 THEN ( SELECT date FROM ( SELECT TOP 1 row_number() OVER ( ORDER BY a.date DESC ) rs , a.date FROM a WHERE a.flag = 1 GROUP BY a.date) s ) END
use CROSS APPLY() to obtain the latest row with flag 1 SELECT * FROM yourtable t CROSS APPLY ( SELECT TOP 1 x.Date as [Last flag on date] FROM yourtable x WHERE x.Party = t.Party AND x.Flag = 1 ORDER BY x.Date desc ) d
Yes it can be done by joining table, if written properly. #Sahi query is also good and simple. Since you were asking for Dynamic LAG() This query may or may not be very performant,but it certainly worth learning. Test this with various sample data and tell me for which scenario it do not work. So that I correct my script accordingly. DECLARE #tab1 TABLE(PARTY CHAR(1),DATE DATE,Flag bit) INSERT INTO #tab1 SELECT 'A','7-24-2018',1 Union ALL SELECT 'A','7-28-2018',0 Union ALL SELECT 'A','7-29-2018',0 Union ALL SELECT 'A','7-29-2018',0 Union ALL SELECT 'B','7-13-2018',1 Union ALL SELECT 'B','7-17-2018',0 Union ALL SELECT 'B','7-18-2018',0 Union ALL SELECT 'C','7-8-2018',1 Union ALL SELECT 'C','7-13-2018',0 Union ALL SELECT 'C','7-19-2018',0 Union ALL SELECT 'C','7-19-2018',0 Union ALL SELECT 'C','7-20-2018',0; WITH cte AS (SELECT *, Row_number() OVER ( partition BY party ORDER BY flag DESC, [date] DESC ) rn FROM #tab1) SELECT *, CASE WHEN flag = 1 THEN [date] ELSE Lag([date], (SELECT TOP 1 a.rn - a1.rn FROM cte a1 WHERE a1.party = a.party)) OVER ( ORDER BY party ) END FROM cte a
split data row wise based on row values
I have a table that stores information in the below format. id, value , property are the columns. I have a requirement now to sum up data based on property. i.e for property column F2 and Value, I need values summed up and displayed as below: Type | Sum Cars | 1892+702+515 Bikes | 1393 +0 + 474.6 Note: I know this is not the way to store data in a table, but table alterations are currently not possible. Appreciate if you could give your inputs on this.
Here's another solution which uses LEAD in case if you are running SQL Server 2012+ (note my comments). -- Sample data DECLARE #yourtable TABLE ( id int identity primary key, -- emulate an index on ID value varchar(100), property varchar(5) ); INSERT #yourtable (value, property) VALUES ('0', 'F2'), ('0', 'V1'), ('0', 'V2'), ('0', 'V3'), ('Cars', 'F2'), ('1892', 'V1'), ('702', 'V2'), ('515', 'V3'), ('Bikes', 'F2'), ('1393', 'V1'), ('0', 'V2'), ('474.6', 'V2'); -- Solution WITH startPoints AS ( SELECT *, rn = ROW_NUMBER() OVER (ORDER BY id) FROM #yourtable ), groups AS ( SELECT value, rn, ttl = ISNULL(LEAD(id,1) OVER (ORDER BY id), (SELECT COUNT(*) FROM #yourtable)+1) - (rn+1) FROM startPoints WHERE property = 'F2' AND value LIKE ('%[^0-9.]%') ) SELECT value, SUM = ( SELECT SUM(CAST(value AS decimal(10,2))) FROM startPoints s WHERE s.rn BETWEEN g.rn+1 AND g.rn+ttl ) FROM groups g;
This looks like a really bad design. It looks like you are using the positions in the table to assign "groupings". Fortunately, you have an id column, so this is possible to do in SQL. Here is the idea: First assign the appropriate F2 property to each row. Then do an aggregation. This following uses outer apply for the first part and group by for the second: select t2.value, sum(case when isnumeric(t.value) = 1 then cast(t.value as decimal(10, 2)) end) as thesum from t outer apply (select top 1 t2.* from t t2 where t2.id <= t.id and t2.property = 'F2' order by t2.id desc ) t2 group by t2.value; This doesn't filter out the first group (all 0's). You can do that with an additional WHERE clause if you like.
Find consecutive free numbers in table
I have a table, containing numbers (phone numbers) and a code (free or not available). Now, I need to find series, of 30 consecutive numbers, like 079xxx100 - 079xxx130, and all of them to have free status. Here is an example how my table looks like: CREATE TABLE numere ( value int, code varchar(10) ); INSERT INTO numere (value,code) Values (123100, 'free'), (123101, 'free'), ... (123107, 'booked'), (123108, 'free'), (... (123130, 'free'), (123131, 'free'), ... (123200, 'free'), (123201, 'free'), ... (123230, 'free'), (123231, 'free'), ... I need a SQL query, to get me in this example, the 123200-123230 range (and all next available ranges). Now, I found an example, doing more or less what I need: select value, code from numere where value >= (select a.value from numere a left join numere b on a.value < b.value and b.value < a.value + 30 and b.code = 'free' where a.code = 'free' group by a.value having count(b.value) + 1 = 30) limit 30 but this is returning only the first 30 available numbers, and not within my range (0-30). (and takes 13 minutes to execute, hehe..) If anyone has an idea, please let me know (I am using SQL Server)
This seems like it works in my dataset. Modify the select and see if it works with your table name. DECLARE #numere TABLE ( value int, code varchar(10) ); INSERT INTO #numere (value,code) SELECT 123100, 'free' WHILE (SELECT COUNT(*) FROM #numere)<=30 BEGIN INSERT INTO #numere (value,code) SELECT MAX(value)+1, 'free' FROM #numere END UPDATE #numere SET code='booked' WHERE value=123105 select * from #numere n1 inner join #numere n2 ON n1.value=n2.value-30 AND n1.code='free' AND n2.code='free' LEFT JOIN #numere n3 ON n3.value>=n1.value AND n3.value<=n2.value AND n3.code<>'free' WHERE n3.value IS NULL
This is usual Island and Gap problem. ; with cte as ( select *, grp = row_number() over (order by value) - row_number() over (partition by code order by value) from numere ), grp as ( select grp from cte group by grp having count(*) >= 30 ) select c.grp, c.value, c.code from grp g inner join cte c on g.grp = c.grp
You can query table data for gaps between booked numbers using following SQL query where SQL LEAD() analytical function is used ;with cte as ( select value, lead(value) over (order by value) nextValue from numere where code = 'booked' ), cte2 as ( select value gapstart, nextValue gapend, (nextValue - value - 1) [number count in gap] from cte where value < nextValue - 1 ) select * from cte2 where [number count in gap] >= 30 You can check the SQL tutorial Find Missing Numbers and Gaps in a Sequence using SQL I hope it helps,
Can't Test it at the moment, but this might work: SELECT a.Value FROM (SELECT Value FROM numere WHERE Code='free' ) a INNER Join (SELECT Value FROM numere WHERE code='free' ) b ON b.Value BETWEEN a.Value+1 AND a.Value+29 GROUP BY a.Value HAVING COUNT(b.Value) >= 29 ORDER BY a.Value ASC The output should be all numbers that have 29 free numbers following (so it's 30 consecutive numbers)
Referencing a previous row value for an arithmetic calculation in SQL Server 2008 R2
I am working with SQL Server 2008 R2 and new to relational database. I need to run a simple calculation but the calculation involves using a previous row value. Example: (Value of X) / ((Value of Y at time t + Value of Y at time t-1) / 2) Example: select (x/[(y#time,t + y#time,t-1)/2]) as 'Value' from datatable select ((c.ACHQ)/(c.RECTQ(row:n) + c.RETQ(row:n-1))/2) as 'AR' from co_ifndq c where c.GVKEY in (select GVKEY from spidx_cst where DATADATE = '2012-03-12' and INDEXID = '500') and c.DATAFMT = 'std' and c.DATADATE > '1990-12-30' order by c.GVKEY, datadate desc
As I understand you want to make a calculation base on a date difference and not really on a row order, right? If so, if you have a table like this CREATE TABLE YourTable( ACHQ float , RECTQ float, DATE datetime) INSERT INTO YourTable VALUES (100,10,'20100101') INSERT INTO YourTable VALUES (200,20,'20110101') INSERT INTO YourTable VALUES (300,30,'20120101') INSERT INTO YourTable VALUES (400,40,'20130101') INSERT INTO YourTable VALUES (500,50,'20140101') INSERT INTO YourTable VALUES (600,60,'20150101') you can do something like this SELECT ((c.ACHQ)/(c.RECTQ + cPreviousYear.RECTQ)/2) as 'AR' FROM YourTable c LEFT JOIN YourTable cPreviousYear ON YEAR(c.Date) - 1 = YEAR(cPreviousYear.Date) I simplified the calculation just to show that you can link the table to itself directly to the row with the wanted date difference and then calculate the value. you can even use ON DATEADD(y, -1, c.Date) = cPrevious.Date if you want the real date diference Sorry if I missed the point.
Assuming x, y and t are all on the same table, try: ;with cte as ( select m.*, row_number() over (order by t) rn from mytable) select t1.t, t1.x / ((t1.y + t0.y)/2) as [value] from cte t1 left join cte t0 on t0.rn = t1.rn-1 EDIT: based on the query supplied: ;with cte as ( select c.*, row_number() over (partition by c.GVKEY order by c.DATADATE) rn from co_ifndq c where c.GVKEY in (select GVKEY from spidx_cst where DATADATE = '2012-03-12' and INDEXID = '500') and c.DATAFMT = 'std' and c.DATADATE > '1990-12-30' ) select t1.GVKEY, t1.DATADATE, t1.ACHQ / ((t1.RETQ + t0.RETQ)/2) as [value] from cte t1 left join cte t0 on t1.GVKEY = t0.GVKEY and t0.rn = t1.rn-1 order by t1.GVKEY, t1.datadate desc
Select and sums from another table. Whats wrong with this SQL?
Whats wrong with this SQL? SELECT Id, (select SUM(VALUE) from SomeTable) AS SumValue, GETDATE() FROM MyTable WHERE SumValue > 0
You cannot use aliased columns in the SELECT clause in the same query, except in ORDER BY. It needs to be subqueried SELECT Id, SumValue, GETDATE() FROM ( SELECT Id, (select SUM(VALUE) from TABLE) AS SumValue FROM MyTable ) X WHERE SumValue > 0 That is the general case. For your specific query, it doesn't make sense because the subquery is not correlated to the outer query, so either NO rows show, or ALL rows show (with the same SumValue). I will simply assume you have simplified the query a lot since a table name of "table" doesn't really work.
I would probably rewrite like this: SELECT a.Id, b.SumValue, GETDATE() as [now] FROM MyTable a Join ( select id, SUM(VALUE) as [SumValue] from [TABLE] Group by id )b on a.Id = b.Id WHERE b.SumValue > 0 This is assuming that the value you are totalling relates to the ID in your table?
right way is SELECT Id, (select SUM(VALUE) from TABLE) AS SumValue, GETDATE() FROM MyTable WHERE (select SUM(VALUE) from TABLE) > 0