T-SQL Query to convert rows to coumns based on mutiple tables - tsql

I have two master tables CompanyMaster, ActivityMaster for a child table CompanyActivities
ActivityMaster
ACTIVITYID ACTIVITYNAME
A1 testActivity
A2 someActivity
A3 otheractivity
A4 someotheractivity
A5 anyotheractivity
CompanyMaster
COMPANYID COMPANYNAME
C1 testcompany
C2 ACompany
C3 MyCompany
C4 SomeCompany
C5 ZCompany
C6 Company123
C7 ComapnyABC
CompanyActivities - The COMPANYID in CompanyActivities is having a primarykey-foreighkey relation ship with COMPANYID in CompanyMaster (primary key table) and ACTIVITYID is having a primarykey-foreighkey relation ship with ACTIVITYID in ActivityMaster(primary key table)
COMPANYID ACTIVITYID
C1 A1
C1 A3
C3 A1
C3 A2
C4 A5
C5 A1
C6 A3
C7 A3
I want to do write a query to get the following output where all the rows in ACTIVITYID column of the ActivityMaster table will be converted to columns
Output
Companies A1 A2 A3 A4 A5
C1 Y N Y N N
C2 N N N N N
C3 Y Y N N N
C4 N N N N Y
C5 Y N N N N
C6 N N Y N N
C7 N N Y N N
The output table displays all the companies as rows in the first column and all the activities are shown as columns that start after the first column, if there is row that contains both ACTIVITYID and COMPANYID it will set to Y in output otherwise it would be set to N
eg- COMPANYID C1 is having an activity ACTIVITYID A1 in CompanyActivities table so the first row in the second column that comes just below A1 and in the right to C1 is set Y, whereas C1 and A2 are not having a row, so the third column in the first row is set to N
I am using C#.net and 4 for loops to achieve the output now which is talking a heavy toll on the performance of the application, So i would like to do this using a query, I have searched for pivot queries, but all the examples i found knows the column names before-hand, which i don't i only get the names of the column names by querying the ActivityMaster.

create table #CompanyMaster (COMPANYID int, COMPANYNAME varchar(30))
create table #ActivityMaster (ACTIVITYID int, ACTIVITYNAME varchar(30))
create table #CompanyActivities (COMPANYID int, ACTIVITYID int)
insert into #CompanyMaster
SELECT 1, 'Company A'
union all
SELECT 2, 'Company B'
insert into #ActivityMaster
SELECT 101, 'Activity X'
union all
SELECT 102, 'Activity Y'
union all
SELECT 103, 'Activity Z'
insert into #CompanyActivities
select 1, 102
union all
select 2, 101
-- build activities column names
--case [Activity X] when 0 then ''N'' else ''Y'' end as [Activity X],
--case [Activity Y] when 0 then ''N'' else ''Y'' end as [Activity Y],
--case [Activity Z] when 0 then ''N'' else ''Y'' end as [Activity Z]
declare #activities nvarchar(max)
set #activities
= (
select 'case [' + ACTIVITYNAME + '] when 0 then ''N'' else ''Y'' end as [' + ACTIVITYNAME + '],' + char(10)
from #ActivityMaster
for xml path('')
)
set #activities = substring(#activities, 0, len(#activities)-1)
declare #activities_for nvarchar(max)
-- build activities column names in for
--[Activity X], [Activity Y], [Activity Z]
set #activities_for
= (
select '[' + ACTIVITYNAME + '],' + char(10)
from #ActivityMaster
for xml path('')
)
set #activities_for = substring(#activities_for, 0, len(#activities_for)-1)
declare #sql nvarchar(MAX) = N'
select COMPANYNAME,
<activities>
From
(select c.COMPANYNAME, a.ACTIVITYNAME,
(case
when ca.ACTIVITYID is not null and ca.COMPANYID is not null then 1
else 0
end) as STATUS
from #CompanyMaster c
cross join #ActivityMaster a
left join #CompanyActivities ca on ca.COMPANYID = c.COMPANYID and a.ACTIVITYID = ca.ACTIVITYID) p
pivot
(
sum(STATUS) for ACTIVITYNAME IN (<activities_for>)
) as pvt
'
set #sql = replace(#sql, '<activities>', #activities)
set #sql = replace(#sql, '<activities_for>', #activities_for)
print #sql
exec sp_executesql #sql
drop table #CompanyMaster
drop table #ActivityMaster
drop table #CompanyActivities

Related

SSRS Expression Split string in rows and column

I am working with SQL Server 2008 Report service. I have to try to split string values in different columns in same row in expression but I can't get the excepted output. I have provided input and output details. I have to split values by space (" ") and ("-").
Input :
Sample 1:
ASY-LOS,SLD,ME,A1,A5,J4A,J4B,J4O,J4P,J4S,J4T,J7,J10,J2A,J2,S2,S3,S3T,S3S,E2,E2F,E6,T6,8,SB1,E1S,OTH AS2-J4A,J4B,J4O,J4P,J4S,J4T,J7,J1O,J2A,S2,S3,J2,T6,T8,E2,E4,E6,SLD,SB1,OTH
Sample 2:
A1 A2 A3 A5 D2 D3 D6 E2 E4 E5 E6 EOW LH LL LOS OTH P8 PH PL PZ-1,2,T1,T2,T3 R2-C,E,A RH RL S1 S2-D S3
Output should be:
Thank you.
I wrote this before I saw your comment about having to do it in the report. If you can explain why you cannot do this in the dataset query then there may be a way around that.
Anyway, here's one way of doing this using SQL
DECLARE #t table (RowN int identity (1,1), sample varchar(500))
INSERT INTO #t (sample) SELECT 'ASY-LOS,SLD,ME,A1,A5,J4A,J4B,J4O,J4P,J4S,J4T,J7,J10,J2A,J2,S2,S3,S3T,S3S,E2,E2F,E6,T6,8,SB1,E1S,OTH AS2-J4A,J4B,J4O,J4P,J4S,J4T,J7,J1O,J2A,S2,S3,J2,T6,T8,E2,E4,E6,SLD,SB1,OTH'
INSERT INTO #t (sample) SELECT 'A1 A2 A3 A5 D2 D3 D6 E2 E4 E5 E6 EOW LH LL LOS OTH P8 PH PL PZ-1,2,T1,T2,T3 R2-C,E,A RH RL S1 S2-D S3'
drop table if exists #s1
SELECT RowN, sample, SampleIdx = idx, SampleValue = [Value]
into #s1
from #t t
CROSS APPLY
spring..fn_Split(sample, ' ') as x
drop table if exists #s2
SELECT
s1.*
, s2idx = Idx
, s2Value = [Value]
into #s2
FROM #s1 s1
CROSS APPLY spring..fn_Split(SampleValue, '-')
SELECT SampleKey = [1],
Output = [2] FROM #s2
PIVOT (
MAX(s2Value)
FOR s2Idx IN ([1],[2])
) p
This produced the following results
If you do not have a split function, here is the script to create the one I use
CREATE FUNCTION [dbo].[fn_Split]
/* Define I/O parameters WARNING! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE! */
(#pString VARCHAR(8000)
,#pDelimiter CHAR(1)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
/*"Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000: enough to cover VARCHAR(8000)*/
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)--10E+1 or 10 rows
,E2(N) AS (SELECT 1 FROM E1 a,E1 b)--10E+2 or 100 rows
,E4(N) AS (SELECT 1 FROM E2 a,E2 b)--10E+4 or 10,000 rows max
/* This provides the "base" CTE and limits the number of rows right up front
for both a performance gain and prevention of accidental "overruns" */
,cteTally(N) AS (
SELECT TOP (ISNULL(DATALENGTH(#pString), 0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM E4
)
/* This returns N+1 (starting position of each "element" just once for each delimiter) */
,cteStart(N1) AS (
SELECT 1 UNION ALL
SELECT t.N + 1 FROM cteTally t WHERE SUBSTRING(#pString, t.N, 1) = #pDelimiter
)
/* Return start and length (for use in SUBSTRING later) */
,cteLen(N1, L1) AS (
SELECT s.N1
,ISNULL(NULLIF(CHARINDEX(#pDelimiter, #pString, s.N1), 0) - s.N1, 8000)
FROM cteStart s
)
/* Do the actual split.
The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found. */
SELECT
idx = ROW_NUMBER() OVER (ORDER BY l.N1)
,value = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l

SQL: Data Cleaning

I am facing a problem which I do not know how to categorize. So, pardon me for the generic title. I have a dataset like:
Table1: Column1, Column2, Column3.
According to my business logic, for a pair of 'Column1 Column2', the Column3 can have only one unique value. So below table is a problematic one because of the second entry:
Table1
Column1 Column2 Column3
A1 B1 R
A1 B1 O << ERROR! for A1-B1 pair only one value on column3 is accepted
A2 B2 R
A2 B3 J
A3 B3 K
A4 B5 K
From above table I would like to find the problematic entries:
A1 B1 R
A1 B1 O
Thanks in advance for your help !
Using your example column names, you can run the following query to just see the Column1/Column2 pairs that have more than 1 value in Column 3.
SELECT Column1, Column2, COUNT(DISTINCT Column3) as Column3
FROM Table1
GROUP BY Column1, Column2
HAVING COUNT(DISTINCT Column3) > 1
You can omit the HAVING line to see the complete list of Column1/Column2 pairs.

How can i query the last data from 3 tables

now i have 3 tables, for example A,B,C
the relation between them is A onetomany B, B onetomany C.
C is a table saved photos
now i want get data from A, but only the last photo each A.
the colomns maybe like this:
table a:
id a_msg
a1 msg in a
a2 msg in a
a3 msg in a
table b:
id b_msg a_id
b1 some data in b a1
b2 some data in b a1
b3 some data in b a2
b4 some data in b a3
table c:
id url createdate c_msg b_id
c1 /file/1.jpg 2014-12-01 06:55:54.600 some data in c b1
c2 /file/2.jpg 2014-12-01 06:55:54.601 some data in c b1
c3 /file/3.jpg 2014-12-01 06:55:54.602 some data in c b1
c4 /file/4.jpg 2014-12-01 06:55:54.603 some data in c b2
c5 /file/5.jpg 2014-12-01 06:55:54.604 some data in c b2
c6 /file/6.jpg 2014-12-01 06:55:54.605 some data in c b3
the result i want get
c_id url createdate c_msg b_msg b_id a_msg a_id
c6 /file/6.jpg 2014-12-01 06:55:54.605 some data in c some data in b b3 msg in a a1
c5 /file/5.jpg 2014-12-01 06:55:54.604 some data in c some data in b b2 msg in a a1
Sorry ,i don't know how to use tool to describle the table,hope you can easily understand what i mean.
if my description is not clear enough,i will edit the question,thank you if anyone can help me
Consider the following as an example :
create table table_a (id int,a_msg text);
create table table_b (id int,b_msg text,a_id int);
create table table_c (id int,url text,createdate timestamp with time zone,c_msg text ,b_id int);
and the data
insert into table_a values (1,'msg in table_a')
,(2,'2nd msg in table_a')
,(3,'3rd msg in table_a');
insert into table_b values (20,'msg in table_b',1)
,(21,'2nd msg in table_b',2)
,(22,'3rd msg in table_b',3);
insert into table_c values (30,'url','2014-12-01 06:55:54.600','msg in table_c',20)
,(31,'url 1','2014-12-01 06:55:54.604','2nd msg in table_c',21)
,(32,'url 2','2014-12-01 06:55:54.605','3rd msg in table_c',22);
to get the result you need to use INNER JOIN and to get the last two data use order by createdate desc limit 2
select c.id,c.url
,c.createdate
,c.c_msg,b.b_msg
,b.id bi_id,a.a_msg
,a.id a_id
from
table_c c inner join table_b b on c.b_id=b.id /* to get data from table_b */
inner join table_a a on b.a_id=a.id /* to get data from table_a */
order by createdate desc limit 2 /* DESC will sort from the highest date time values and LIMIT 2 will return two rows */
>SQLFIDDLE DEMO WITH OP'S DATA

How to get GroupName and GroupMembers in a Row

I have a table with GroupMembers and GorupName in 2 Columns as
Col 1 GroupMember Col2 GroupName
A1 A
A2 A
B1 B
B2 B
C1 C
C2 C
How to get output result as
A - GroupName
A1 - GroupMember
A2 - GroupMember
B
B1
B2
C
C1
C2
Here I am trying to get the GroupName and its GroupMembers in a single Column
;with Groups AS
(
select distinct GroupName from YourTableName
)
,OrderedGroups AS
(
select GroupName, ROW_NUMBER() Over(order by GroupName) R from Groups
)
,RankedData As
(
select T.GroupMember, T.GroupName, OG.R from YourTableName T
inner join OrderedGroups OG on T.GroupName = OG.GroupName
)
select GroupMember, R from RankedData
union
select GroupName, R from RankedData
order by R

jdbc data comparision

I want to compare content of 15 columns in two rows.
I am using db2 9 with jdbc.
Can I use a sql to get a result like "match or not match"
And How can I get columns differs?
You can use the EXCEPT operator to do this.
In the example below, I'm using common table expressions to fetch a the single rows (assuming, in this case, that id is the primary key.
with r1 as (select c1, c2, ..., c15 from t where id = 1),
r2 as (select c1, c2, ..., c15 from t where id = 2)
select * from r1
except
select * from r2
If this returns 0 rows, then the rows are identical. If it returns a row, then the two rows differ.
If you really want the result to be 'MATCH' or 'NOT MATCH':
with r1 as (select c1, c2, ..., c15 from t where id = 1),
r2 as (select c1, c2, ..., c15 from t where id = 2),
rs as (select * from r1 except select * from r2)
select
case when count(*) = 0 then 'MATCH'
else 'NOT MATCH'
end as comparison
from
rs;