Replace multiple Value from another table - sql-server

I have 2 tables.
Table1
ID | String
1 | "A TEST B VALUE"
2 | "C TEST D DENT B"
Table 2
ID | Name | Value
1 | A | 1
1 | B | 2
2 | B | 3
2 | C | 4
2 | D | 5
The result I am hoping to get is
ID | String
1 | "1 TEST 2 VALUE"
2 | "4 TEST 5 5ENT 3"
I am trying to do this using with clause recursive
with QueryTable as (
select id, cast(String as nvarchar(max)) as 'String' ,1 as 'RN'
from [TABLE1]
group by id, String
union all
select a.id, cast(replace(a.String, b.Name, b.value) as nvarchar(max)) as 'Query', RN+1
from QueryTable a
inner join [TABLE2] b on a.id = b.id
)
Thank you so very much.

The solution Found in below link, so with cte recursive but compare between source and destination row num seem to solve the problem.
https://dba.stackexchange.com/questions/237182/how-to-replace-multiple-parts-of-a-string-with-data-from-multiple-rows
if anyone interested I can post the final solution

Related

Fetching data from two tables in oracle

I have two result sets :
Set 1:
STUDENT| COUNT
------ | ------
mohit | 4
Rohit | 2
Tanvi | 2
Jhanvi | 1
Set 2:
STUDENT| COUNT_STAR
------ | ------
mohit | 2
Rohit | 3
Tanvi | 1
Arjun | 1
Abhay | 3
Abhi | 1
Expected Result Set :
STUDENT| COUNT | COUNT_STAR
------ | ------ | ----------
mohit | 4 | 2
Rohit | 2 | 3
Tanvi | 2 | 1
Arjun | na | 1
Abhay | na | 3
Abhi | na | 1
Jhanvi | 1 | na
Can someone help me with the SQL Query for this ?
you need a union for get the distinct name from both the table
and left join for get the values for count an count_star
select T.STUDENT , table1.count, table2.count_star
from (
select STUDENT
from table1
UNION
select STUDENT
from table2
) T
left join table1 on table1.student = t.student
left join table2 on table1.student = t.student
Use a FULL OUTER JOIN to join two overlapping result sets:
select coalesce(table1.student, table2.student) as student
, nvl( table1.count, 'na') as count
, nvl( table2.star_count, 'na') as star_count
from table1
full outer join table2
on table1.student = table2.student
you can use FULL OUTER JOIN to get the required result-
SELECT DECODE (a.STUDENT, NULL, b.STUDENT, a.STUDENT) STUDENT,
a.COUNT,
b.count_star
FROM table1 a FULL OUTER JOIN table2 b ON a.STUDENT = b.STUDENT;
Hope this helps.
Following SQL is tested with Oracle 12G:
SELECT COALESCE (T1.STUDENT, T2.STUDENT) AS STUDENT,
DECODE (T1.COUNT, NULL, 'na', T1.COUNT) COUNT,
DECODE (T2.COUNT_STAR, NULL, 'na', T2.COUNT_STAR) COUNT_STAR
FROM TABLE1 T1
FULL OUTER JOIN TABLE2 T2 ON T1.STUDENT = T2.STUDENT;

How to fractionally split a column while doing a inner join in SQL Server

I wanted to basically split a column:
Table A has 3 columns: Id, Name, Number
Table b has 3 columns: Id, Name, School
I join on the basis of Id.
Suppose that the number is 100, the Name might have multiple schools (suppose 3), so I want to equally split 100 by 3 and do the join.
Sample Final Table
Id Name School Number
---------------------------
1 ABC A 33.33
1 ABC B 33.33
1 ABC C 33.33
You can get the count for each id using count(*) over(partition by a.Id) and divide the number by that.
test setup: rextester: http://rextester.com/JQK48793
create table a (id int, name char(3), number decimal(9,2))
insert into a
values (1,'ABC',100.0)
create table b (id int, name char(3), school char(1))
insert into b values
(1,'ABC','A')
,(1,'ABC','B')
,(1,'ABC','C')
query:
select
a.Id
, a.Name
, b.School
, Number = (a.Number+.0) / count(*) over (partition by a.Id)
from a
inner join b
on a.Id = b.Id
results:
+----+------+--------+------------------+
| Id | Name | School | Number |
+----+------+--------+------------------+
| 1 | ABC | A | 33,3333333333333 |
| 1 | ABC | B | 33,3333333333333 |
| 1 | ABC | C | 33,3333333333333 |
+----+------+--------+------------------+

Combine Parent-Child Rows - TSQL

lI am trying to flatten/combine rows from a table with a parent-child hierarchy. I'm trying to identify the beginning and the end of each 'link' - so if a is linked to b, b is linked to c, and then c is linked to d, I want the output to link a to d.
I'm trying my best to avoid using a procedure with loops, so any advice would be much appreciated!
The original dataset and the required output is as follows:
personID | form | linkedform
---------|---------|---------
1 | a | b
1 | b | c
1 | c | d
1 | d | NULL
2 | e | f
2 | f | g
2 | g | NULL
2 | h | i
2 | i | NULL
3 | j | NULL
3 | k | l
3 | l | NULL
Desired output:
personID | form | linkedform
---------|---------|---------
1 | a | d
2 | e | g
2 | h | i
3 | j | NULL
3 | k | l
Each personID can have multiple links, and a link can be made of just one or multiple forms.
-- use a recursive cte to build the hierarchy
-- start with [linkedform] = null and work your way up
;WITH cte AS
(
SELECT *, [form] AS [root],
1 AS [Level]
FROM Table1
WHERE [linkedform] IS NULL
UNION ALL
SELECT t1.*,
[root],
[Level] + 1
FROM Table1 t1
JOIN cte ON cte.form = t1.linkedform
)
-- level 1 will be the last element, use row_number to get the first element
-- join the two together based on last and first level, that have the same personid and root ([linkedform] = null)
SELECT cte.personId,
cte2.form,
cte.form
FROM cte
JOIN ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY personId, [root] ORDER BY Level DESC) Rn
FROM cte) cte2
ON cte2.Rn = cte.Level
AND cte2.personId = cte.personId
AND cte2.root = cte.root
WHERE cte.[Level] = 1
ORDER BY cte.personId, cte2.form

SQL Server making rows into columns

I'm trying to take three tables that I have and show the data in a way the user asked me to do it. The tables look like this. (I should add that I am using MS SQL Server)
First Table: The ID is varchar, since it's an ID they use to identify assets and they use numbers as well as letters.
aID| status | group |
-----------------------
1 | acti | group1 |
2 | inac | group2 |
A3 | acti | group1 |
Second Table: This table is fixed. It has around 20 values and the IDs are all numbers
atID| traitname |
------------------
1 | trait1 |
2 | trait2 |
3 | trait3 |
Third Table: This table is used to identify the traits the assets in the first table have. The fields that have the same name as fields in the above tables are obviously linked.
tID| aID | atID | trait |
----------------------------------
1 | 1 | 1 | NAME |
2 | 1 | 2 | INFO |
3 | 2 | 3 | GOES |
4 | 2 | 1 | HERE |
5 | A3 | 2 | HAHA |
Now, the user wants the program to output the data in the following format:
aID| status | group | trait1 | trait2 | trait 3
-------------------------------------------------
1 | acti | group1 | NAME | INFO | NULL
2 | inac | group2 | HERE | NULL | GOES
A3 | acti | group1 | NULL | HAHA | NULL
I understand that to achieve this, I have to use the Pivot command in SQL. However, I've read and tried to understand it but I just can't seem to get it. Especially the part where it asks for a MAX value. I don't get why I need that MAX.
Also, the examples I've seen are for one table. I'm not sure if I can do it with three tables. I do have a query that joins all three of them with the information I need. However, I don't know how to proceed from there. Please, any help with this will be appreciated. Thank you.
There are several ways that you can get the result, including using the PIVOT function.
You can use an aggregate function with a CASE expression:
select t1.aid, t1.status, t1.[group],
max(case when t2.traitname = 'trait1' then t3.trait end) trait1,
max(case when t2.traitname = 'trait2' then t3.trait end) trait2,
max(case when t2.traitname = 'trait3' then t3.trait end) trait3
from table1 t1
inner join table3 t3
on t1.aid = t3.aid
inner join table2 t2
on t3.atid = t2.atid
group by t1.aid, t1.status, t1.[group];
See SQL Fiddle with Demo
The PIVOT function requires an aggregate function this is why you would need to use either the MIN or MAX function (since you have a string value).
If you have a limited number of traitnames then you could hard-code the query:
select aid, status, [group],
trait1, trait2, trait3
from
(
select t1.aid,
t1.status,
t1.[group],
t2.traitname,
t3.trait
from table1 t1
inner join table3 t3
on t1.aid = t3.aid
inner join table2 t2
on t3.atid = t2.atid
) d
pivot
(
max(trait)
for traitname in (trait1, trait2, trait3)
) piv;
See SQL Fiddle with Demo.
If you have an unknown number of values, then you will want to look at using dynamic SQL to get the final result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(traitname)
from Table2
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT aid, status, [group],' + #cols + '
from
(
select t1.aid,
t1.status,
t1.[group],
t2.traitname,
t3.trait
from table1 t1
inner join table3 t3
on t1.aid = t3.aid
inner join table2 t2
on t3.atid = t2.atid
) x
pivot
(
max(trait)
for traitname in (' + #cols + ')
) p '
execute sp_executesql #query;
See SQL Fiddle with Demo

Select column based on whether a specific row in another table exists

Question is similar to this one How to write a MySQL query that returns a temporary column containing flags for whether or not an item related to that row exists in another table
Except that I need to be more specific about which rows exists
I have two tables: 'competitions' and 'competition_entries'
Competitions:
ID | NAME | TYPE
--------------------------------
1 | Example | example type
2 | Another | example type
Competition Entries
ID | USERID | COMPETITIONID
---------------------------------
1 | 100 | 1
2 | 110 | 1
3 | 110 | 2
4 | 120 | 1
I want to select the competitions but add an additional column which specifies whether the user has entered the competition or not. This is my current SELECT statement
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
(CASE
WHEN e.ID IS NOT NULL AND e.USERID = #userid THEN 1
ELSE 0
END
) AS 'ENTERED'
FROM competitions AS c
LEFT OUTER JOIN competition_entries AS e
ON e.COMPETITIONID = c.ID
My desired result set from setting the #userid parameter to 110 is this
ID | NAME | TYPE | ENTERED
-------------------------------------
1 | Example | example type | 1
2 | Another | example type | 1
But instead I get this
ID | NAME | TYPE | ENTERED
-------------------------------------
1 | Example | example type | 0
1 | Example | example type | 1
1 | Example | example type | 0
2 | Another | example type | 1
Because it's counting the entries for all user ids
Fixing your query
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
MAX(CASE
WHEN e.ID IS NOT NULL AND e.USERID = #userid THEN 1
ELSE 0
END
) AS 'ENTERED'
FROM competitions AS c
LEFT OUTER JOIN competition_entries AS e ON e.COMPETITIONID = c.ID
GROUP BY
c.[ID],
c.[NAME],
c.[TYPE]
An alternative is to rewrite it using EXISTS which is pretty much the same but may be easier to understand.
BTW, using single quotes on the column name is deprecated. Use square brackets.
SELECT
c.[ID],
c.[NAME],
c.[TYPE],
CASE WHEN EXISTS (
SELECT *
FROM competition_entries AS e
WHERE e.COMPETITIONID = c.ID
AND e.USERID = #userid) THEN 1 ELSE 0 END [ENTERED]
FROM competitions AS c

Resources