Increment values by column into rows-- SQL Server [closed] - sql

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have the following type of data
CREATE TABLE #tmp
(
Room [NVARCHAR](50) NOT NULL,
iking INT,
iqueen INT,
isingle INT,
idouble INT
)
INSERT INTO #tmp
VALUES ('Marriot', 0, 1, 2, 1),
('Hilton', 1, 2, 0, 1)
I tried Cross Apply and case statements
I add data into temp table and wrote 4 cross apply functions for each column
King
SELECT tk.Room, tk.iking, Type = CONCAT('BED', t.n)
INTO #tempking1
FROM #tmp tk
CROSS APPLY
(SELECT TOP (tk.iking)
n = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o) t
ORDER BY tk.Room;
--select * from #tempking1
Queen
SELECT
tq.Room, tq.iQueen,
Type = CASE WHEN ROOM in (SELECT Distinct ROOM FROM #tempking1)
THEN CONCAT('BED', t.n + 1)
ELSE CONCAT('BED', t.n)
END
INTO #tempQueen1
FROM #tmp tq
CROSS APPLY
(SELECT TOP (tq.iQueen)
n = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o) t
ORDER BY tq.Room;
--select * from #tempqueen1
Single
SELECT
tq.Room, tq.isingle,
Type = CASE WHEN ROOM IN (SELECT Distinct ROOM FROM #tempking1)
THEN CONCAT('BED', t.n + 1)
WHEN ROOM IN (SELECT Distinct ROOM FROM #tempqueen1)
THEN CONCAT('BED', t.n + 1)
ElSE CONCAT('BED', t.n)
END
INTO #tempsingle1
FROM #tmp tq
CROSS APPLY
(SELECT TOP (tq.isingle)
n = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o) t
ORDER BY tq.Room;
--select * from #tempsingle1
Double
SELECT
tq.Room, tq.isingle,
Type = CASE WHEN ROOM IN (SELECT Distinct ROOM FROM #tempking1)
THEN CONCAT('BED', t.n + 1)
WHEN ROOM IN (SELECT Distinct ROOM FROM #tempqueen1)
THEN CONCAT('BED', t.n + 1)
WHEN ROOM IN (SELECT Distinct ROOM FROM #tempsingle1)
THEN CONCAT('BED', t.n + 1)
ELSE CONCAT('BED', t.n)
END
INTO #tempdouble1
FROM #tmp tq
CROSS APPLY
(SELECT TOP (tq.isingle)
n = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o) t
ORDER BY tq.Room;
--select * from #tempDouble1
SELECT Room, Type, 'King' AS Descp FROM #tempKing1
UNION ALL
SELECT Room, Type, 'Queeen' AS Descp FROM #tempQueen1
UNION ALL
SELECT Room, Type, 'Single' AS Descp FROM #tempsingle1
UNION ALL
SELECT Room, Type, 'Double' AS Descp FROM #tempDouble1
but I got
My excepted output is
Could you please help me

You can use UNPIVOT operator to fix your solution. Try this script:
drop TABLE #tmp
go
drop TABLE #Hotel
go
CREATE TABLE #tmp
(
Room nvarchar(30) NOT NULL
,iking int
,iqueen int
,isingle int
,idouble int
)
CREATE TABLE #Hotel
(
Room NVARCHAR(30)
,RoomType NVARCHAR(30)
,Total INT
)
Insert into #tmp Values ('Marriot', 0,1,2,1),('Hilton', 1,2,0,1)
INSERT INTO #Hotel
SELECT Room, RoomType, Total
FROM
(SELECT Room,iking,iqueen,isingle,idouble
FROM #tmp) p
UNPIVOT
(Total FOR RoomType IN
(iking,iqueen,isingle,idouble)
)AS unpvt
SELECT Room
,RoomType
--,'Bed'+CAST(Number AS VARCHAR) AS [Desc]
,'Bed'+CAST(ROW_NUMBER() OVER(PARTITION BY Room ORDER BY RoomType,Number) AS VARCHAR) AS [Desc]
,'Bed'+CAST(Total AS varchar) [Desc2]
FROM #Hotel
INNER JOIN master.dbo.spt_values N ON Total>=N.Number AND N.type='P' AND number<>0

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

Missing Value Search in string seperated by comma

Please help me if it is possible in sql.
Hello, may anyone please share their expertise, i am not very sure whether it is possible in SQL
SIZE OF TABLE: 5GB
I am trying to see invalid VALIDATION_ID present in a child table for a given PRODUCT_LINE
SO in below scenario 114 is not present for PRODUCT_LINE Passive in master table but present in child table.
DECLARE #CHILD TABLE
(
PRODUCT_LINE VARCHAR (50),
COMPONENT VARCHAR (50),
MODEL VARCHAR (50),
YEARS VARCHAR (50),
VALIDATION_ID VARCHAR (50)
)
INSERT #CHILD
SELECT 'PASSIVE','RESISTOR','CARBON','2005','V114' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','V098, E009, V034' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','V201' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','V201,V098,V114' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005',null UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','null,V098,E009' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','null,V114' UNION ALL
SELECT * FROM #CHILD
DECLARE #PARENT TABLE
(
PRODUCT_LINE VARCHAR (50),
VALIDATION_ID VARCHAR (50)
)
INSERT #PARENT
SELECT 'PASSIVE','V098' UNION ALL
SELECT 'PASSIVE','E009' UNION ALL
SELECT 'PASSIVE','V201' UNION ALL
SELECT 'PASSIVE','V034'
EXPECTED OUTPUT
PRODUCT_LINE COMPONENT MODEL YEARS VALIDATION_ID INVALID_VALIDATION_ID
PASSIVE RESISTOR CARBON 2005 V114 V114
PASSIVE RESISTOR CARBON 2005 V201,V098,V114 V114
'PASSIVE','RESISTOR','CARBON','2005','null,V114',V114
Thanks ....
you will definately required a CSV splitter. There are many, just search for it. Here I am using Jeff's DelimitedSplit8K()
SELECT c.*, INVALID_VALIDATION_ID = v.Item
FROM #CHILD c
CROSS APPLY dbo.DelimitedSplit8K(c.VALIDATION_ID, ',') v
WHERE NOT EXISTS
(
SELECT *
FROM #PARENT p
WHERE p.PRODUCT_LINE = c.PRODUCT_LINE
and p.VALIDATION_ID = LTRIM(v.Item)
)
if there are more than one invalid ID, it will appear as multiple line. If you want the multiple invalid ID to appear as CSV, you will need additional query to concatenate it. Let me know if you required that
EDIT : Updated query to show invalid ID in CSV
; WITH
CTE AS
(
SELECT c.*, INVALID_VALIDATION_ID = v.Item,
RN = DENSE_RANK() OVER (ORDER BY PRODUCT_LINE, COMPONENT, MODEL, YEARS, VALIDATION_ID)
FROM #CHILD c
CROSS APPLY dbo.DelimitedSplit8K(c.VALIDATION_ID, ',') v
WHERE c.VALIDATION_ID IS NOT NULL -- edit [2]
AND NOT EXISTS
(
SELECT *
FROM #PARENT p
WHERE p.PRODUCT_LINE = c.PRODUCT_LINE
and p.VALIDATION_ID = LTRIM(v.Item)
)
)
SELECT DISTINCT
PRODUCT_LINE, COMPONENT, MODEL, YEARS, VALIDATION_ID,
INVALID_VALIDATION_ID = STUFF(csv, 1, 1, '')
FROM CTE c
CROSS APPLY
(
SELECT ',' + x.INVALID_VALIDATION_ID
FROM CTE x
WHERE x.RN = c.RN
ORDER BY INVALID_VALIDATION_ID
FOR XML PATH('')
) i (csv)
You Need to Try This...
SELECT *
FROM
(
SELECT c.*, INVALID_VALIDATION_ID = x.Value
FROM #CHILD c
CROSS APPLY dbo.Split(c.VALIDATION_ID, ',') x
WHERE NOT EXISTS
(
SELECT *
FROM #PARENT p
WHERE p.PRODUCT_LINE = c.PRODUCT_LINE
and p.VALIDATION_ID = x.Value
)
) AS Q
WHERE INVALID_VALIDATION_ID IS NOT NULL AND INVALID_VALIDATION_ID != 'null'
Here dbo.split function is ..
CREATE FUNCTION [dbo].[Split]
(#List varchar(8000),#SplitOn varchar(5))
RETURNS #RtnValue table
(Id int identity(1,1),Value nvarchar(100))
AS
BEGIN
Set #List = Replace(#List,'''','')
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select
Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
try this......

SQL replace from list

I'm trying to figure our how I can replace a string using data from another table
I have a table that looks like this:
Id Translation
1 Peter
2 Sandra
3 Olga
Now I want to select all and replace the translations using a list that looks like this:
Original New
e #
r ?
lg *%
So that the select list looks like this:
Id Translation
1 P#t#?
2 Sand?a
3 O*%a
So, for each translation, I need to have a REPLACE(Translation,Original,New).
Or in other words: I need to go through every "Translation" in my first list and make another loop in my replacement table to see what to replace
Bare in mind that the first list has 25'000 rows and the second has 50'000, so I can't just type it by hand :)
EDIT
Just to clarify:
The Original and New from my look up table can be both letters and words so the table can looks like this:
Original New
one two
three fifty
sun moon
To do this in one query, you need to use a recursive CTE. Something like:
with trans as (
select t.original, t.new, row_number() over (order by t.original) as seqnum,
count(*) over () as cnt
from translations
),
t as (
select tt.id, tt.string, replace(tt.string, trans.original, trans.new) as replaced,
seqnum + 1 as seqnum, cnt
from totranslate tt join
trans
on trans.id = 1
union all
select t.id, t.string, replace(t.string, trans.original, trans.new),
seqnum + 1 as seqnum, cnt
from t join
trans
on t.seqnum = trans.id
where t.seqnum <= t.cnt
)
select t.id, t.string, t.replaced
from t
where seqnum = cnt;
You can use a UDF:
CREATE FUNCTION [dbo].[Translate]
(
-- Add the parameters for the function here
#Str nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE #Result nvarchar(max) = #Str;
SELECT #Result = replace(#Result,Original,New) from dbo.Mappings order BY Pos;
RETURN #Result;
END
Here I assumed the table containing translations is called dbo.Mappings and beside the Original and New columns you need another column Pos int which will be used to determine the order in which the translations are applied (to address the problems mentioned by #Thorsten Kettner in comments)
Also with recursive cte:
DECLARE #translations TABLE
(
Id INT ,
Translation NVARCHAR(20)
)
INSERT INTO #translations
VALUES ( 1, 'Peter' ),
( 2, 'Sandra' ),
( 3, 'Olga' )
DECLARE #replacements TABLE
(
Original VARCHAR(2) ,
New VARCHAR(2)
)
INSERT INTO #replacements
VALUES ( 'e', '#' ),
( 'r', '?' ),
( 'lg', '*%' );
WITH cte1 AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY (SELECT 1)) rn
FROM #translations CROSS JOIN #replacements),
cte2 AS (SELECT Id, rn, REPLACE(Translation, Original, New) AS NTranslation
FROM cte1
WHERE rn = 1
UNION ALL
SELECT c2.Id, c2.rn + 1, REPLACE(c2.NTranslation, c1.Original, c1.New)
FROM cte1 c1
JOIN cte2 c2 ON c2.Id = c1.Id AND c2.rn + 1 = c1.rn)
SELECT * FROM cte2
WHERE rn = (SELECT COUNT(*) FROM #replacements)
ORDER BY Id
EDIT:
WITH cte1 AS (SELECT t.*, p.Id AS Old, p.Code, ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY (SELECT 1)) rn
FROM translations t CROSS JOIN Property p),
cte2 AS (SELECT Id, rn, REPLACE(Trans, Old, Code) AS NTranslation
FROM cte1
WHERE rn = 1
UNION ALL
SELECT c2.Id, c2.rn + 1, REPLACE(c2.NTranslation, c1.Old, c1.Code)
FROM cte1 c1
JOIN cte2 c2 ON c2.Id = c1.Id AND c2.rn + 1 = c1.rn)
SELECT * FROM cte2
WHERE rn = (SELECT COUNT(*) FROM Property)
ORDER BY Id
Here is something I worked out that will allow you to replace multiple characters with one specified string.
[Split2] is stolen from https://blogs.msdn.microsoft.com/amitjet/2009/12/11/convert-comma-separated-string-to-table-4-different-approaches/
USE <Your Database>
GO
CREATE FUNCTION [dbo].[Split2]
(
#strString varchar(4000)
)
RETURNS #Result TABLE
(
RID INT IDENTITY(0,1) Primary Key
,Value varchar(4000)
)
AS
BEGIN
WITH StrCTE(start, stop) AS
(
SELECT 1, CHARINDEX(',' , #strString )
UNION ALL
SELECT stop + 1, CHARINDEX(',' ,#strString , stop + 1)
FROM StrCTE
WHERE stop > 0
)
INSERT INTO #Result
SELECT SUBSTRING(#strString , start, CASE WHEN stop > 0 THEN stop - start ELSE 4000 END) AS stringValue
FROM StrCTE
RETURN
END
GO
USE <Your Database>
GO
CREATE FUNCTION [dbo].[MultiReplace]
(
#MyString varchar(MAX)
,#RepChars varchar(4000)
,#NewChars varchar(4000)
)
RETURNS varchar(MAX)
AS
BEGIN
DECLARE #CurRow int = 0
DECLARE #MaxRow int
SELECT #MaxRow = MAX(RID)
FROM dbo.split2 ( #RepChars )
WHILE #CurRow <= #MaxRow
BEGIN
SELECT #MyString = REPLACE(#MyString,VALUE,#NewChars)
FROM dbo.split2 ( #RepChars )
WHERE RID = #CurRow
SET #CurRow = #CurRow + 1
END
RETURN (#MyString);
END
GO
In this example I replace each character with no space
SELECT [dbo].[MultiReplace]('6th month 2016-06 (test / requested)',',1st,2nd,3rd,4th,5th,6th,0,1,2,3,4,5,6,7,8,9,(,),/,-,+, ','')
Result:
monthtestrequested
I hope this is useful for you.

TSQL Recursive Query Update Temp Table

I have a query that is recursively going through my employee ORG and getting a list of all people that report up to the VP. This query is working as intended:
DECLARE #pit AS DATETIME = GETDATE();
DECLARE #table TABLE (
mgrQID VARCHAR (64) ,
QID VARCHAR (64) ,
NTID VARCHAR (64) ,
FullName VARCHAR (256),
lvl INT ,
metadate DATETIME ,
totalCount INT );
WITH empList (mgrQID, QID, NTID, FullName, lvl, metadate)
AS (SELECT TOP 1 mgrQID,
QID,
NTID,
FirstName + ' ' + LastName,
0,
Meta_LogDate
FROM dbo.EmployeeTable_Historical
WHERE QID IN (SELECT director
FROM dbo.attritionDirectors)
AND Meta_LogDate <= #pit
ORDER BY Meta_LogDate DESC
UNION ALL
SELECT b.mgrQID,
b.QID,
b.NTID,
b.FirstName + ' ' + b.LastName,
lvl + 1,
b.Meta_LogDate
FROM empList AS a CROSS APPLY dbo.Fetch_DirectsHistorical_by_qid (a.QID, #pit) AS b)
-- Insert into the counts table
INSERT INTO #table (mgrQID, QID, NTID, FullName, lvl, metadate, totalCount)
SELECT empList.mgrQID,
empList.QID,
empList.NTID,
empList.FullName,
empList.lvl,
empList.metadate,
'0'
FROM empList
ORDER BY lvl
OPTION (MAXRECURSION 10);
As you can see, I have a table column called totalCount which I set to 0 in the first recursive query.
I now have a second query that goes through all of the people in that temp table and finds the total direct reports up to them.
For example if a Director Had 3 Managers and Each Manager has 3 Employees it would be 12 people reporting up to the director; the 9 employees and the 3 managers.
This comes from the query below:
;WITH a
AS (SELECT mgrQID AS direct,
QID
FROM #table AS t
WHERE QID IN (SELECT QID
FROM #table)
UNION ALL
SELECT a.direct,
t.QID
FROM #table AS t
INNER JOIN
a
ON t.mgrQID = a.QID)
--subtracting 1 because it is also counting the manager
SELECT direct,
count(*) - 1 AS totalCount
FROM a
GROUP BY direct
OPTION (MAXRECURSION 10);
My question is...
How can I update #temp totalCount with the count I get from the second query? QID and Direct are the 2 fields in common amongst the two.
Try this:
update t
set t.totalCount = a.count(*) - 1
from a
join #temp t
on a.Direct = t.QID
group by a.direct, t.QID
option (maxrecursion 10)

Finding unique characters from a table field for all rows

How to find the number of distinct character used in the field having multiple rows.
For example, if there are two rows having data like abcd and eaafg* then distinct character used are abcdefg*.
Try this one -
INSERT INTO #temp (txt)
VALUES ('abcd3'), ('abcdefg*')
SELECT disword = (
SELECT DISTINCT dt.ch
FROM (
SELECT ch = SUBSTRING(t.mtxt, n.number + 1, 1)
FROM [master].dbo.spt_values n
CROSS JOIN (
SELECT mtxt = (
SELECT txt
FROM #temp
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)'
)
) t
WHERE [type] = N'p'
AND number <= LEN(mtxt) - 1
) dt
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)'
)
Example (edited):
SET NOCOUNT ON;
DECLARE #temp TABLE (txt VARCHAR(8000))
INSERT INTO #temp (txt)
VALUES ('abcd'), ('abcdefg*'), (REPLICATE('-', 8000)), (REPLICATE('+', 8000))
DECLARE #t TABLE (i BIGINT)
DECLARE
#i BIGINT = 1
, #l BIGINT = (
SELECT SUM(LEN(txt))
FROM #temp
)
WHILE (#i <= #l) BEGIN
INSERT INTO #t (i)
VALUES (#i), (#i+1), (#i+2), (#i+3), (#i+4), (#i+5), (#i+6), (#i+7), (#i+8), (#i+9)
SELECT #i += 10
END
SELECT disword = (
SELECT DISTINCT dt.ch
FROM (
SELECT ch = SUBSTRING(t.mtxt, n.i, 1)
FROM #t n
CROSS JOIN (
SELECT mtxt = (
SELECT txt
FROM #temp
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)'
)
) t
) dt
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)'
)
Have a look a t this solution -
SELECT
dt.ch
, cnt = COUNT(1)
FROM (
SELECT ch = SUBSTRING(t.mtxt, n.i, 1)
FROM #t n
CROSS JOIN (
SELECT mtxt = (
SELECT txt
FROM #temp
FOR XML PATH(N''), TYPE, ROOT).value(N'root[1]', N'NVARCHAR(MAX)')
) t
) dt
WHERE dt.ch != ''
GROUP BY dt.ch
ORDER BY cnt DESC
Here are questions that may refer to what you are asking:
How do I get distinct characters of string column in mssql?
and
SQL: how to get all the distinct characters in a column, across all rows
With tallys:
DECLARE #t TABLE (s NVARCHAR(MAX))
INSERT INTO #t
VALUES ('abcd'), ('abcdefg*')
;WITH tally AS(SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) i
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t1(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t2(n))
SELECT
( SELECT DISTINCT sb
FROM tally
CROSS APPLY ( SELECT SUBSTRING(s, i, 1) sb FROM #t ) ca
WHERE sb <> ''
FOR XML PATH(N'') , TYPE , ROOT).value(N'root[1]', N'NVARCHAR(MAX)')