SQL - displaying row where the next numeric value is also available - sql

I have the following query:
SELECT CAST(year_week AS NUMERIC) as year_week FROM web_details where location = ''JF'' AND property_id = ''FARM''
which produces the following results.
YEAR_WEEK
201035
201036
201037
201039
201041
201044
201045
201048
What I actually want is to produce a set of results which only displays values if the consecutive value is available - so producing the following results...
YEAR_WEEK
201035
201036
201044
To add another spanner into the works, the column year_week is not a numeric value so has needed to be converted.
Thanks

SELECT
CAST(year_week AS NUMERIC) as year_week
FROM
web_details wd
WHERE
EXISTS(
SELECT
year_week
FROM
web_details wd2
WHERE
wd2.year_week = CASE(RIGHT(wd.year_week, 2))
WHEN '48' THEN CAST((CAST(LEFT(wd.year_week,4) AS INT) + 1) AS VARCHAR(4)) + '01'
ELSE LEFT(wd.year_week,4) + CAST((CAST(RIGHT(wd.year_week,2) AS INT) + 1) AS VARCHAR(2))
END
)

Basically, my approach is that you calculate another column that contains the next year_week value, and the join it to itself.
WITH myCTE AS (
SELECT year_week, CONVERT(VARCHAR(4),DATEPART(year,CONVERT(datetime,LEFT(year_week,4)) + (RIGHT(year_week,2) + 1) * 7 )) + RIGHT( '000' + CONVERT(VARCHAR(2),DATEPART(MONTH,CONVERT(datetime,LEFT(year_week,4)) + (RIGHT(year_week,2) + 1) * 7)),2) next_year_week
FROM web_details
WHERE ..........
)
SELECT T1.year_week, T2.year_week
FROM myCTE T1
INNER JOIN myCTE T2 ON T1.next_year_week = T2.year_week

Related

How to complete and fill in gaps between dates in SQL?

I have data in Redshift that I'm aggregating to the Year-Quarter level i.e. number of items by Year-Quarter
I need to show a continuous trend and hence I need to fill-in the gaps in Year-Quarter. The picture below should give a clearer idea of my current data and desired output.
How can I achieve this in Redshift SQL?
A query like this should do the trick:
create table test (yq int, items int);
INSERT INTO test Values (20201,10),(20204, 15),(20213, 25),(20222, 30);
with recursive quarters(q) as (
select min(yq) as q
from test
union all
select decode(right(q::text, 1), 4, q + 7, q + 1) as q
from quarters
where q < (select max(yq) from test)
)
select q as yq, decode(items is null, true,
lag(items ignore nulls) over (order by q), items) as items
from test t
right join quarters q
on t.yq = q.q
order by q;
It uses a recursive CTE to generate the quarters range needed, right joins this with the source data, and then uses a LAG() window function to populate the items if the value is NULL.
This is known as forward filling values:
CREATE TABLE #Temp
(
[YQ] nvarchar(5),
[items] int
)
INSERT INTO #Temp Values ('20201',10),('20204', 15),('20213', 25),('20222', 30)
---------------------------------------------------------------------------------
DECLARE #start int, #end int, #starty int, #endy int
SELECT #start=1, #end=4
SELECT #starty=MIN(Substring(YQ,0,5)), #endy=MIN(Substring(YQ,0,5)) from #Temp
;With cte1(y) as
(
Select #starty as y
union all
Select y + 1
from cte1
where y <= #endy + 1
)
, cte2(n) as
(
Select #start as n
union all
Select n + 1
from cte2
where n < #end
)
SELECT t1.YQ AS 'Year-Quarter',
CASE WHEN t2.items is null then (SELECT TOP 1 MAX(items) from #Temp WHERE items is not null and YQ < t1.YQ) ELSE t2.items END AS '# Items'
FROM
(
SELECT CAST(cte1.y AS nvarchar(4)) + CAST(cte2.n AS nvarchar(1)) AS YQ
FROM cte1, cte2
) t1
LEFT JOIN #Temp t2 ON t2.YQ = t1.YQ
WHERE t1.YQ <= (SELECT MAX(YQ) FROM #Temp)
ORDER BY t1.YQ, t2.items

Find a missing values from a table

Is there a way to search in varchar column.
I got table called x with row named par and data in:
150/RXRPR1/18/0020642
150/RXRPR1/18/0020640
150/RXRPR1/18/0020639
151/RXRPR1/18/0020638
151/RXRPR1/18/0020637
151/RXRPR1/18/0020636
151/RXRPR1/18/0020634
The row is missing
150/RXRPR1/18/0020641
151/RXRPR1/18/0020635
How to write a SQL statement to search the table for search the missing data?
The data is of type varchar and I have permissions to select only in database
You can use classical gaps detection in the right 7 chars on your numbers, partitioned by the left 14 chars. Use LEAD function to find the value of the next record and check is the difference between the current value and next value greater than 1. This will detect the gaps and you can calculate the start and the end of the gap by adding 1 to the current value and subtracting one from the next value. Something like this:
declare #t table(col varchar(50))
insert into #t(col) values
('150/RXRPR1/18/0020642'),
('150/RXRPR1/18/0020640'),
('150/RXRPR1/18/0020639'),
('151/RXRPR1/18/0020638'),
('151/RXRPR1/18/0020637'),
('151/RXRPR1/18/0020636'),
('151/RXRPR1/18/0020634')
SELECT
gapStart = left([current], 14) + right('000000' + cast(cast(right([current], 7) as int) + 1 as varchar(10)), 7)
,gapEnd = left([next], 14) + right('000000' + cast(cast(right([next], 7) as int) - 1 as varchar(10)), 7)
FROM
(
SELECT
[current] = col
,[next] = LEAD(col) OVER (partition by left(col, 14) ORDER BY col)
FROM #t
) tmp
WHERE cast(right([next], 7) as int) - cast(Right([current], 7) as int) > 1;
You can try this query:
Please replace #MINVAL,#MAXVAL with respective values.
declare #t table([VALUE] varchar(50))
insert into #t([VALUE]) values
('150/RXRPR1/18/0020642'),
('150/RXRPR1/18/0020640'),
('150/RXRPR1/18/0020639'),
('151/RXRPR1/18/0020638'),
('151/RXRPR1/18/0020637'),
('151/RXRPR1/18/0020636'),
('151/RXRPR1/18/0020634')
DECLARE #MINVAL INT = 20634,
#MAXVAL INT = 20642;
WITH cte
AS (SELECT #MINVAL VAL
UNION ALL
SELECT val + 1 VAL
FROM cte
WHERE val < #MAXVAL)
SELECT missing
FROM (SELECT *,
Replace (Lead([VALUE]) OVER ( ORDER BY val)
, CONVERT (INT, RIGHT (Lead([VALUE]) OVER (ORDER BY val), 7)),
val)
MISSING
FROM cte
LEFT JOIN #t X
ON CONVERT (INT, RIGHT (X.[VALUE], 7)) = cte.val) X
WHERE [VALUE] IS NULL OPTION ( MAXRECURSION 10000)

Updating database column with string built based on value of another column

I have a table with a column called Days. The Days column stores a comma delimited string representing days of the week. For example the value 1,2 would represent Sunday, Monday. Instead of storing this information as a comma delimited string, I want to convert it to JSON and store it in a column called Frequency in the same table. For example, a record with the Days value of 1,2 should be updated to store the following in it's Frequency column:
'{"weekly":"interval":1,"Sunday":true,"Monday":true,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}'
I found a way to do this using a case statement assuming that there is only one digit in the Days column like so:
UPDATE SCH_ITM
SET
FREQUENCY =
CASE
WHEN SCH_ITM.DAYS = 1 THEN '{"weekly":{"interval":1,"Sunday":true,"Monday":false,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 2 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":true,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 3 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":true,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 4 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":false,"Wednesday":true,"Thursday":false,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 5 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":false,"Wednesday":false,"Thursday":true,"Friday":false,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 6 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":true,"Saturday":false}}'
WHEN SCH_ITM.DAYS = 7 THEN '{"weekly":{"interval":1,"Sunday":false,"Monday":false,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":true}}'
END
WHERE SCH_TYPE = 'W';
However I cannot seem to figure out an effecient way to handle converting a value such as 1,5 into the correct JSON representation. Obviously I could write out every possible permutation, but surely is a better way?
Okay this will give you what you have asked for
create table test (days varchar(20), frequency varchar(500))
insert into test(days) values('1'),('2'),('3'),('4'),('5'),('6'),('7'),('1,5')
update test set frequency = '{"weekly":{"interval":1,'
+ '"Sunday": ' + case when days like '%1%' then 'true' else 'false' end + ','
+ '"Monday": ' + case when days like '%2%' then 'true' else 'false' end + ','
+ '"Tuesday": ' + case when days like '%3%' then 'true' else 'false' end + ','
+ '"Wednesday": ' + case when days like '%4%' then 'true' else 'false' end + ','
+ '"Thursday": ' + case when days like '%5%' then 'true' else 'false' end + ','
+ '"Friday": ' + case when days like '%6%' then 'true' else 'false' end + ','
+ '"Saturday": ' + case when days like '%7%' then 'true' else 'false' end + '}}'
select * from test
Though of course e.g. Days = '1234' will produce the same as '1,2,3,4' - as will 'Bl4arg3le12' for that matter. If Days is a string, you can put '8' which is meaningless?
Really it sounds like you need an extra table or two:
If "MyTable" is the table with the Days column, add a Days table with the days of the week, then a MyTableDays table to link MyTable entries to days - for the 1,5 example, there would be two rows in MyTableDays
With the help of a parse function and an cross apply
;with cteDays As (Select ID,Name From (Values(1,'Sunday'),(2,'Monday'),(3,'Tuesday'),(4,'Wednesday'),(5,'Thursday'),(6,'Friday'),(7,'Saturday')) D(ID,Name))
Update YourTable Set Frequency = '{"weekly":"interval":1,'+String+'}}'
From YourTable A
Cross Apply (
Select String = Stuff((Select ','+String
From (
Select String='"'+Name+'":'+case when RetVal is null then 'false' else 'true' end
From [dbo].[udf-Str-Parse](A.Days,',') A
Right Join cteDays B on RetVal=ID) N
For XML Path ('')),1,1,'')
) B
Select * from YourTable
Updated Table
Days Frequency
1,2 {"weekly":"interval":1,"Sunday":true,"Monday":true,"Tuesday":false,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}
1,2,3 {"weekly":"interval":1,"Sunday":true,"Monday":true,"Tuesday":true,"Wednesday":false,"Thursday":false,"Friday":false,"Saturday":false}}
The UDF if needed
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>'+ Replace(#String,#Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')

T-SQL Truncate text and add number at the end to avoid duplicates

I need to truncate data from a column to 10 characters. However, I cannot have any duplicates, so I want any duplicates to end with ~1 for the first duplicate, ~2 for the second duplicate. Here's an example of what I have:
Column
------
The ABC Company Inc.
The ABC Cooperative
XYZ Associates LLC.
I'd like the result to be:
Column
------
The ABC ~1
The ABC ~2
XYZ Associ
The end doesn't have to be ~1 or ~2, I just need something to make it unique after truncating. There may be more than 3 or 4 duplicates after truncating.
So far, I'm just truncating and editing the table manually:
update Table set Column = Left(Column, 10) where len(Column) > 10
First, you care about the first 8 characters, not the first 10, because you need to reserve slots for the additional number.
Assuming that you have fewer than 10 repeats, you can do this:
with toupdate as (
select t.*,
row_number() over (partition by left(col, 8) order by (select null)) as seqnum,
count(*) over (partition by left(col, 8) ) as cnt
from t
update toupdate
set col = (case when cnt = 1 then left(col, 10)
else left(col, 8) + '~' + cast(seqnum as char(1));
The same idea can be used for a select.
Declare #Table Table (Column1 varchar(50))
Insert into #Table values
('The ABC Company Inc.'),
('The ABC Cooperative'),
('XYZ Associates LLC.')
Select NewColumn = Concat(substring(Column1,1,10),' ~',Row_Number() over (Partition By substring(Column1,1,10) Order by Column1))
From #Table
Returns
NewColumn
The ABC Co ~1
The ABC Co ~2
XYZ Associ ~1
The numbers are noisy, so I only add them when necessary:
select case when _r > 1
then Company + '~' + cast(_r as varchar(5))
else Company end as Company
from (
select Company
, ROW_NUMBER() over (partition by Company order by Company) as _r
from(
select left(Company, 10) as Company
from MyTable
) x
) y
order by Company
Company
--------------
The ABC Co
The ABC Co~2
XYZ Associ
Assuming your table is COMPANY and the field is CompanyName.....
You'll have to tweek but hope it helps..
SELECT SUBSTRING( Q.Comp, 1, 5) + '~' + CONVERT(nvarchar(4), Row) as NewFieldValue FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY SUBSTRING( C.CompanyName, 1, 6) ORDER BY SUBSTRING( C.CompanyName, 1, 6)) AS Row,
SUBSTRING( C.CompanyName, 1, 6) as Comp
FROM COMPANY C
)Q
DECLARE #Table TABLE (Column1 varchar(50))
INSERT INTO #Table VALUES
('The ABC Company Inc.')
, ('The ABC Cooperative')
, ('XYZ Associates LLC.')
, ('Acme')
, ('Ten Char 123')
, ('Ten Char 132')
, ('Ten Char 231')
;WITH FLen
AS (
SELECT Column1, LEFT(LEFT(Column1,13) + SPACE(13),13) + CHAR(164) AS Column2
FROM #Table
)
,TenCharPD -- Includes possible duplicates
AS (
SELECT Column1, LEFT(Column2,8) +
RIGHT('0' + CAST (
(ASCII(SUBSTRING(Column2, 9,1)) +
ASCII(SUBSTRING(Column2,10,1)) +
ASCII(SUBSTRING(Column2,11,1)) +
ASCII(SUBSTRING(Column2,12,1)) +
ASCII(SUBSTRING(Column2,13,1)))%100
AS NVARCHAR(2)),2) AS Column2
FROM Flen
)
,CullPD
AS (
SELECT Column1, Column2,
ROW_NUMBER() OVER (PARTITION BY Column2 ORDER BY Column2) AS rowx
FROM TenCharPD
)
UPDATE t1
SET Column1 = LEFT(Column2,9) +
CASE rowx
WHEN 1 THEN RIGHT(Column2,1)
ELSE CHAR(rowx + CAST (RIGHT(Column2,1) AS INT) * 5 + 63)
END
FROM #Table t1
JOIN CullPD cpd
ON t1.Column1 = cpd.Column1
SELECT * FROM #Table

How to change a column to use ranges like 1 to 999 instant of id's like, 1 going downwards in SQL

I have a table node. I want the same id numbers of that particular nodetype to be in the same range for example 1 to 9999 and another nodetype to be in another range from 1000 to 1999. Can you give me an example of how to do this. This is a SQL query.
DECLARE #T TABLE (NODEDE_ID BIGINT)
INSERT INTO #T VALUES
(170999000),
(172677777),
(177333335)
;WITH CTE AS
(
SELECT NODEDE_ID,ROW_NUMBER() OVER (ORDER BY NODEDE_ID) [ROW_NUM]
FROM #T
)
SELECT NODEDE_ID,
CAST(CASE WHEN (ROW_NUM > 1)
THEN (ROW_NUM - 1) * 1000
ELSE 1
END AS VARCHAR) + ' to ' +
CAST(CASE WHEN (ROW_NUM > 1)
THEN ((ROW_NUM - 1) * 1000) + 999
ELSE 999
END AS VARCHAR) [RANGE]
FROM CTE