t-sql grouping query - sql-server

based on the following table
Name
---------
A
A
A
B
B
C
C
C
I want to add another column to this table called 'OnGoing' and the values should alternate for each group of names.
There are only two values 'X' and 'Y'. So the table will look like
Name OnGoing
----------------
A X
A X
A X
B Y
B Y
C X
C X
C X
how to write such a query that can alternate the values for each group of names.

How about something like
DECLARE #Table TABLE(
Name VARCHAR(10)
)
INSERT INTO #Table SELECT 'A'
INSERT INTO #Table SELECT 'A'
INSERT INTO #Table SELECT 'A'
INSERT INTO #Table SELECT 'B'
INSERT INTO #Table SELECT 'B'
INSERT INTO #Table SELECT 'C'
INSERT INTO #Table SELECT 'C'
INSERT INTO #Table SELECT 'C'
SELECT *,
CASE
WHEN RowNum % 2 = 0
THEN 'Y'
ELSE 'X'
END
FROM #Table t INNER JOIN
(
SELECT Name,
ROW_NUMBER() OVER (ORDER BY Name) RowNum
FROM #Table
GROUP BY Name
) sub ON t.Name = sub.Name

Alter your table to include the new column:
ALTER TABLE YourTable ADD
OnGoing char(1) NULL
GO
and then try this:
DECLARE #YourTable table (Name char(1), OnGoing char(1))
INSERT #YourTable Values ('A',NULL)
INSERT #YourTable Values ('A',NULL)
INSERT #YourTable Values ('A',NULL)
INSERT #YourTable Values ('B',NULL)
INSERT #YourTable Values ('B',NULL)
INSERT #YourTable Values ('C',NULL)
INSERT #YourTable Values ('C',NULL)
INSERT #YourTable Values ('C',NULL)
;WITH TableRows AS
(
SELECT
*, ROW_NUMBER() OVER(ORDER BY Name) AS RowID
FROM (SELECT DISTINCT
*
FROM #YourTable
) dt
)
UPDATE y
SET OnGoing=CASE
WHEN r.RowID % 2 = 1 THEN 'X'
ELSE 'Y'
END
FROM #YourTable y
INNER JOIN TableRows r ON y.Name=r.Name
SELECT * FROM #YourTable
OUTPUT:
Name OnGoing
---- -------
A X
A X
A X
B Y
B Y
C X
C X
C X
(8 row(s) affected)

This will do the trick:
select
t1.Name,
sub1.OnGoing
from
TableOne t1
INNER JOIN
(select
Name,
CASE RANK() over (order by Name) % 2
WHEN 0 THEN 'Y'
WHEN 1 THEN 'X'
END as OnGoing
from
TableOne
group by Name) sub1 ON sub1.Name = t1.Name
This is the actual output from my test:
Name OnGoing
---- -------
A X
A X
A X
B Y
B Y
C X
C X
C X

Related

need help in forming sql query

ItemId Name parentId
1 A null
2 b null
3 c 1
4 d 2
5 e 3
6 f 4
7 g 2
hi i need help in create sql query. I have a table that contain 3 column itemid ,name ,parentitemid. i need a sql query that result parent child relation.if parentitemid id null then it means root .please help
i need data like.
<1><3><5></5> </3></1>
For example you can use:
WITH HierarchicalTable
AS
(
SELECT Id, ParentId, Name, 0 as [Level]
FROM YourTable
WHERE ParentId IS NULL
UNION ALL
SELECT YourTable.Id, YourTable.ParentId, YourTable.Name, [Level] + 1
FROM YourTable
JOIN HierarchicalTable ON HierarchicalTable.Id = YourTable.ParentId
)
SELECT [Level], Name FROM HierarchicalTable
This is excessively complicated solution, but it will work for your problem:
DECLARE #temp TABLE (ItemId int, Name char(1), parentId int,l int)
DECLARE #xml TABLE (s nvarchar(max), e nvarchar(max), parentId int, itemid int)
DECLARE #l int
;WITH cte AS (
SELECT *
FROM (VALUES
(1, 'a', NULL),(2, 'b', NULL),(3, 'c', 1),(4, 'd', 2),(5, 'e', 3),(6, 'f', 4),(7, 'g', 2)
) as t(ItemId, Name, parentId)
--Here we create recursive cte to obtain levels of nesting
), res AS (
SELECT *,
1 [Level]
FROM cte c
where parentId IS null
UNION ALL
SELECT c.*,
[Level]+1
FROM res r
INNER JOIN cte c
ON c.parentId = r.ItemId
)
--put results into temp table
INSERT INTO #temp
SELECT *
FROM res
--obtain max level
SELECT #l = MAX(l)
FROM #temp
--from max level to 1 begin
WHILE #l > 0
BEGIN
--if there is nodes with same parentid - concatinating them
UPDATE x
SET x.e = x.e + v.s + v.e
FROM #xml x
INNER JOIN #xml v
ON v.parentId = x.parentId and v.e !=x.e;
--here we merge table with results
-- first run <e></e>
-- next run <c><e></e></c>
-- next run <a><c><e></e></c></a>
MERGE #xml AS target
USING (
SELECT '<'+ Name +'>' as s,'</'+ Name + '>' as e, parentId, ItemId
FROM #temp
WHERE l = #l
) as source
ON target.parentid = source.itemid
WHEN NOT MATCHED THEN INSERT VALUES (source.s, source.e, source.parentId, source.ItemId)
WHEN MATCHED THEN
UPDATE
SET target.s = source.s + target.s,
target.e = target.e + source.e,
target.parentid = source.parentid,
target.itemid = source.itemid;
--next level down
SET #l = #l - 1
END
SELECT x --CAST(x as xml)
FROM (
SELECT s+e as x,
DENSE_RANK() OVER (PARTITION BY itemid ORDER BY s ASC) as rn
--need that column to obtain first one of every string for itemid
FROM #xml
) as c
WHERE c.rn = 1
--FOR XML PATH ('')
Output will be:
x
<a><c><e></e></c></a>
<b><d><f></f></d><g></g></b>
If you remove -- near FOR XML PATH ('') and change this SELECT x --CAST(x as xml) to this SELECT CAST(x as xml) in last query you will get this:
<a>
<c>
<e />
</c>
</a>
<b>
<d>
<f />
</d>
<g />
</b>

SQL Server select (top) two rows into two temp variables

I have a query which results in two or more rows (just one column) and I want to catch the first row value into first temp variable and second row value into second temp variable without using multiple times the select top 1 and select top 1 order by desc
Something like this;
Select row1 value into #tempvariable1, row2 value into #tempvariable2 from blah blah
You need somehow to identify the row (I am using a row ID in the example below, ordering by value - you can order by id or something else):
DECLARE #DataSource TABLE
(
[value] VARCHAR(12)
);
INSERT INTO #DataSource
VALUES ('value 1')
,('value 2')
,('value 3');
DECLARE #tempVariable1 VARCHAR(12)
,#tempVariable2 VARCHAR(12);
WITH DataSource ([value], [rowID]) AS
(
SELECT [value]
,ROW_NUMBER() OVER (ORDER BY [value])
FROM #DataSource
)
SELECT #tempVariable1 = IIF([rowID] = 1, [value], #tempVariable1)
,#tempVariable2 = IIF([rowID] = 2, [value], #tempVariable2)
FROM DataSource;
SELECT #tempVariable1
,#tempVariable2;
You can use a CTE where you will get the X values you need and then select from it:
declare #data table(id int);
insert into #data(id) values(8), (6), (4), (3);
with vals(id, n) as (
Select top(2) id, ROW_NUMBER() over(order by id)
From #data
)
Select #A = (Select id From vals Where n = 1)
, #B = (Select id From vals Where n = 2)
You could also use PIVOT:
Select #A = [1], #B = [2]
From (
Select id, ROW_NUMBER() over(order by id)
From #data
) v(id, n)
PIVOT (
max(id) FOR n in ([1], [2])
) as piv
You have two options
Let's say we test case is build as below
create table dbo.Test
(
value varchar(100) not null
)
GO
insert into dbo.Test
values
('A'),('B'),('NO THIS ONE'),('NO THIS ONE'),('NO THIS ONE')
GO
Now let's say you fetch your data as below
select t.value
from dbo.Test t
where t.value != 'NO THIS ONE'
GO
The first and easier option is to save the data in a temp table
declare #results as Table (value varchar(100))
insert into #results
select t.value
from dbo.Test t
where t.value != 'NO THIS ONE'
you still use TOP 1 BUT not in the entire data, only in the results.
Use TOP 1 to find the first result and a second TOP 1 where value is different from the first.
declare #A varchar(100), #B varchar(100)
set #A = (select top 1 r.value from #results r)
set #B = (select top 1 r.value from #results r where r.value != #A)
select #A, #B
GO
This approach have the advantage of performance.
Of course that don't work great if both values are equal. You can fix it by using a top 1 and ordering in the inverse order.
There's a better alternative using rownumber.
It works because if you set a variable when returning multiple rows the varible sticks with the last one (in fact it's reseted for each row iteration).
The case statement makes sure the variable #A is seted only on the first row iteration.
declare #A varchar(100), #B varchar(100)
/* This way #B receives the last value and #A the first */
select #B = t.value,
#A = (case when ROW_NUMBER() OVER(order by t.Value) = 1
then t.Value else #A
end)
from dbo.Test t
where t.value != 'NO THIS ONE'
select #A, #B

Apply a series of values from one table to a series of rows in another

Suppose I have table #A with a series of rows where there's a column with NULL values for those rows, and I have table #B with (potentially fewer, or more) rows with values, how can I apply #B's values to #A's rows to fill in the NULLs, without changing #B? (Without using a cursor, I mean; fairly trivial with a cursor.) #A and #B are both in-memory (table variables) and will have only a small number of rows, so we don't have to worry about big data sets.
E.g., if I have:
DECLARE #A TABLE
(
x INT,
y NVARCHAR(20)
)
INSERT INTO #A (y) VALUES ('aaa'), ('bbb'), ('ccc')
DECLARE #B TABLE
(
x INT
)
INSERT INTO #B (X) values (1), (2)
Now I have #A:
x y
----------- -----------
NULL aaa
NULL bbb
NULL ccc
and #B:
x
-----------
1
2
...and I want to update #A based on #B so it has:
x y
----------- -----------
1 aaa
2 bbb
NULL ccc
...ideally in that order (e.g., the lowest value in #B becomes the x for the lowest y in #A, then the next, etc.).
I feel like there's a fairly simple way to do this, but I'm not getting there...
This query will be helpful to get expected result.
DECLARE #A TABLE
(
x INT,
y VARCHAR(10)
)
INSERT INTO #A (y) SELECT ('aaa') UNION SELECT('bbb') UNION SELECT('ccc')
DECLARE #B TABLE
(
x INT
)
INSERT INTO #B (X) SELECT (1) UNION SELECT (2)
UPDATE A SET x = B.x FROm
(SELECT *, ROW_NUMBER() OVER (ORDER BY y) AS RoW1 FROM #A) AS A
INNER JOIN
(SELECT *, ROW_NUMBER() OVER (ORDER BY x) AS RoW1 FROM #B) AS B
ON A.RoW1 = B.RoW1
SELECT * FROm #A
This worked
DECLARE #A TABLE
(
x INT,
y NVARCHAR(20)
)
INSERT INTO #A (y) VALUES ('aaa'), ('bbb'), ('ccc')
DECLARE #B TABLE
(
x INT
)
INSERT INTO #B (X) values (1), (2)
Select * into #X from #B
DECLARE #Val1 INT
WHILE 0 < (Select COUNT(*) from #X)
BEGIN
SET ROWCOUNT 1
Select #Val1 = MIN(x) from #X
UPDATE #A set x = #Val1 where y = (Select MIN(y) from #A where x is null)
Delete #X where x = #Val1
if 0 = (Select Count(*) from #A where x is null)
break
END
SET ROWCOUNT 0
Select * from #A
Its probably not elegant, but it works

String Replace column data with data from another table

I have two tables:
TableA:
ID Values
---------------
1 Q
2 B
3 TA
4 BS
TableB:
RawValue Value
------------------
[1][4] QBS
[2][1][3] BQTA
I need to generate TableB values with its given RawValues. each [X] in rawvalue is the ID coulmn of TableA and shoud be replace with its value .
[1][4] means that Value of TableA with has ID of 1 (Q) and Value of TableA with has ID of 4 (BS) then should equal to QBS.
can anyone suggest a way to do it?
this is what I have already tried:
update tableb set value=replace(rawvalue,'[' + (select id from tablea where id = cast(replace(replace(rawdata,'[',''),']','') as int)) + ']',
(select values from tablea where id = cast(replace(replace(rawdata,'[',''),']','') as int)))
By the way: this is still in test process and I can totally change tables, rowvalue format and replacement methods if anyone has a better idea.
declare #tableA table (id int, value varchar(50))
insert into #tableA (id, value)
select 1, 'Q' union all
select 2, 'B' union all
select 3, 'TA' union all
select 4, 'BS'
declare #tableB table (rawdata varchar(255), value varchar(255))
insert into #tableB (rawdata)
select '[1][4]' union all -- QBS
select '[2][1][3]' -- BQTA
update b
set value = (
select a.value + ''
from #tableA a
cross apply (select charindex ('[' + cast (a.id as varchar(50)) + ']', b.rawdata) as pos) p
where pos > 0
order by pos
for xml path('')
)
from #tableB b
select * from #tableB
P.S. I would recommend not to name field similar to reserved keywords (I mean Values).
Turn RawValue into XML, shred the XML to get one row for each value in RawValue and join to TableA to get the value.
Use the for xml path() trick to concatenate the values from TableA.
update TableB
set Value = (
select T.Value as '*'
from (
select row_number() over(order by T2.X) as SortOrder,
TableA.Value
from (select cast(replace(replace(TableB.RawValue, '[', '<x>'), ']', '</x>') as xml)) as T1(X)
cross apply T1.X.nodes('x') as T2(X)
inner join TableA
on TableA.ID = T2.X.value('text()[1]', 'int')
) as T
order by T.SortOrder
for xml path('')
)
SQL Fiddle

How to add two text row on same column in T-SQL

How can I add two text row on same column, or any alternative aggregate function which can apply sum() on text columns.
id Name
1 A
1 B
2 C
group by id
result like this.
id Names
1 A,B
2 C
Try this:
declare #t table(id int, name varchar(50))
insert #t values(1, 'A')
insert #t values(1, 'B')
insert #t values(2, 'C')
select t.id,
,STUFF((
select ',' + [name]
from #t t1
where t1.id = t.id
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [Names]
from #t t
group by t.id
Result:
id Names
----------- --------------
1 A,B
2 C

Resources