Join query on comma separated values - sql

This is my 1st table :
this is another table on which i want to perform join operation :
I want to retrieve first_name for "activity_cc" column
For example, I want to show Pritam,Niket for activity_id=2
How can I retrieve those values?

From http://mikehillyer.com/articles/an-introduction-to-database-normalization/
The first normal form (or 1NF) requires that the values in each column
of a table are atomic. By atomic we mean that there are no sets of
values within a column.
Your database design violates the first normal form of database design. It is a simply unworkable design and it must be changed (and frankly the database designer who created this should be fired as this is gross incompetence) or there will be severe performance problems and querying will always be difficult. There is a reason why the very first rule of database design is never store more than one piece of information in a field.
Yes you could use some hack methods to get the answer you want, but they will cause performance issues and they are the wrong thing to do. A hack to fix this data into a related table used one-time is fine, a hack to continuallly query your database is simply a poor choice. It will cost less time in the long run to fix this cancer at the heart of your database right now. But in general the process to fix this is to split the data out into a related table using some version of fn_split (look up the various implementations of this for a script to create the function). You can use a temp table in your query or do the right thing and fix the database.

If you want to retrieve the result on the basis of Join then why don't you join your both tables on the "registration_id" by using inner-join. And please clearify me you want to perform the join on the active_cc, but its actually not present in your second table. So how you preform join in that case.

I completely agree with #HLGEM, but to solve this particular problem cost will be high.
I had given a try to want you want to achive here. Please modify the join if needed.
Let me know if any further help needed.
Sample Schema
create table tableC (ACTIVITY_ID int, REG_ID int,PROJ_ID int,DOSS_ID int,ACTIVITY_TO int, ACTIVITY_CC varchar(500))
insert into tableC select 4, 1,1,1,1, '3,4';
insert into tableC select 5, 2,2,2,2, '5,6';
insert into tableC select 6, 3,3,3,3, '3,5';
create table tableD (REG_ID int , FIRST_NAME VARCHAR(100), LAST_NAME VARCHAR(100))
insert into tableD select 3, 'Pritam', 'Sharma';
insert into tableD select 4, 'Pratik', 'Gupta';
insert into tableD select 5, 'Niket', 'Vaidya';
insert into tableD select 6, 'Ajinkya', 'Satwa';
Sample Query
with names as
(
select C.ACTIVITY_ID,C.ACTIVITY_CC
,Names = D.FIRST_NAME
from tableC C
inner join tableD D on charindex(cast(D.REG_ID as varchar), C.ACTIVITY_CC) > 0
)
select
C.ACTIVITY_ID,C.REG_ID,PROJ_ID,DOSS_ID,ACTIVITY_TO,ACTIVITY_CC
,Names = stuff
(
(
select ',' + Names
from names n
where n.ACTIVITY_ID = D.REG_ID
for xml path('')
)
, 1
, 1
, ''
)
from tableD D
inner join tableC C on C.ACTIVITY_ID = D.REG_ID
Added to SQLFiddle also

Considering Pratik's structure
CREATE TABLE tableC
(
ACTIVITY_ID int,
REG_ID int,
PROJ_ID int,
DOSS_ID int,
ACTIVITY_TO int,
ACTIVITY_CC varchar(500)
);
INSERT INTO tableC select 4, 1,1,1,1, '3,4';
INSERT INTO tableC select 5, 2,2,2,2, '5,6';
INSERT INTO tableC select 6, 3,3,3,3, '3,5';
CREATE TABLE tableD
(
REG_ID int,
FIRST_NAME VARCHAR(100),
LAST_NAME VARCHAR(100)
);
INSERT INTO tableD select 3, 'Pritam', 'Sharma';
INSERT INTO tableD select 4, 'Pratik', 'Gupta';
INSERT INTO tableD select 5, 'Niket', 'Vaidya';
INSERT INTO tableD select 6, 'Ajinkya', 'Satwa';
You can do this:
SELECT tableD.FIRST_NAME
FROM tableD
JOIN tableC ON tableC.ACTIVITY_CC LIKE CONCAT('%', tableD.REG_ID, '%')
GROUP BY tableD.FIRST_NAME;
OR
SELECT FIRST_NAME
FROM tableD, tableC
WHERE tableC.ACTIVITY_CC LIKE CONCAT('%', tableD.REG_ID, '%')
GROUP BY tableD.FIRST_NAME;

Related

faster way to retrieve parent values in SQL

Here is a description of my problem with dummy data:
I have a table in SQL Server as below:
Table
id parentid extid Isparent
0 a m 0
1 a m 1
2 a s 0
3 a s 0
4 b q 1
5 b z 0
for each group of records with the same parentid, there is only one record with Isparent = 1.
for each record, I want to find the extid of their parents.
So for id = 0, the parent record is id=1, and extid=m for id=1 is the value I need.
Here is the output I want.
childid parentid child_extid parent_extid
0 a m m
1 a m m
2 a s m
3 a s m
4 b q q
5 b z q
I'm doing this with a self join, but since the table is large, the performance is really slow, I also need to do this multiple times for a few different tables which makes things even worse.
SELECT
a.Id AS 'ChildId',
a.parentid As 'ParentId',
a.extid AS 'child_extid ',
b.extid AS 'parent_extid '
FROM Table a
LEFT JOIN Table b ON (a.parentid = b.parentid)
WHERE b.isparent = 1
Just wondering if there is a better way to do this.
Thanks!
This method of design is incredibly unorthodox. not only is this structure incapable of representing true hierarchy structures, but it also appears to represent a trinary relationship rather than a binary one. If you have control over the design of this data, and this was not your intent, I can help you reformat it for your intent if desired. Until then, this is a very basic representation of what you might be looking for, but falls short because there are unanswered questions about the intent of the data, such as what happens if you generate a row with the values ParentID = a, ExitID = y, IsParent = 1?
Having said that, here's a crack at it. Please note that this execution will be a self-semi join, and will require an index to work properly. This also excludes an order by clause because of the question above.
The code below will be in TSQL format until the DBMS is clarified.
CREATE FUNCTION ParentExitID (#ChildID INT)
AS
BEGIN
RETURN (
SELECT TOP 1 a.ParentID
FROM SampleTable A
WHERE EXISTS (
SELECT 1
FROM SampleTable B
WHERE A.ParentID = B.ParentID
AND A.IsParent = 1
AND B.ChildID = #ChildID
)
END
The "faster" way is to use a relational database, relationally and employ normalization in the design. What the question is demonstrating is cramming as much as possible into a table without any normalization and then building complex (and inefficient) querying logic on top of it.
create table #thing ( id int ); --id is PK
create table #ext ( id varchar(1) ); -- id is PK
create table #thingext ( thing int, ext varchar(1) ); -- thing,ext is PK
create table #parent ( id varchar(1), ext varchar(1)); -- id is PK, ext is FK
create table #thingparent ( thing int, parent varchar(1)); -- thing,parent is PK
insert into #thing values (0),(1),(2),(3),(4),(5);
insert into #ext values ('m'),('s'),('q'),('z');
insert into #thingext values (0,'m'),(1,'m'),(2,'s'),(3,'s'),(4,'q'),(5,'z');
insert into #parent values ('a','m'),('b','q');
insert into #thingparent values (0,'a'),(1,'a'),(2,'a'),(3,'a'),(4,'b'),(5,'b');
select t.id as childid, p.ext as extid
from #thing t
join #thingparent tp on t.id = tp.thing
join #parent p on tp.parent = p.id
What's getting jumbled up in the complexity of the original question is that extid is dependent upon the parent primary key but it is not dependent upon the child primary key. And a model has to reflect that.
Not sure if it would really speed it up.
But you can run this without a WHERE clause by putting that condition on b.isparent in the JOIN.
Example using a table variable:
declare #Table table (id int identity(0,1) primary key, parentid varchar(30), extid varchar(30), isparent bit);
insert into #Table (parentid, extid, isparent) values
('a','m',0),
('a','m',1),
('a','s',0),
('a','s',0),
('b','q',1),
('b','z',0);
SELECT
a.Id AS 'ChildId',
a.parentid AS 'ParentId',
a.extid AS 'child_extid',
b.extid AS 'parent_extid'
FROM #Table a
LEFT JOIN #Table b ON (a.parentid = b.parentid and b.isparent = 1);
But the Explain Plan will probably be the same as in your query.
Adding an non-unique index on parentid could speed things up.

How can I create a new table based on merging 2 tables without joining certain values?

I asked a question earlier, but I wasn't really able to explain myself clearly.
I made a graphic to hopefully help explain what I'm trying to do.
I have two separate tables inside the same database. One table called 'Consumers' with about 200 fields including one called 'METER_NUMBERS*'. And then one other table called 'Customer_Info' with about 30 fields including one called 'Meter'. These two meter fields are what the join or whatever method would be based on. The problem is that not all the meter numbers in the two tables match and some are NULL values and some are a value of 0 in both tables.
I want to join the information for the records that have matching meter numbers between the two tables, but also keep the NULL and 0 values as their own records. There are NULL and 0 values in both tables but I don't want them to join together.
There are also a few duplicate field names, like Location shown in the graphic. If it's easier to fix these duplicate field names manually I can do that, but it'd be cool to be able to do it programmatically.
The key is that I need the result in a NEW table!
This process will be a one time thing, not something I would do often.
Hopefully, I explained this clearly and if anyone can help me out that'd be awesome!
If any more information is needed, please let me know.
Thanks.
INSERT INTO new_table
SELECT * FROM
(SELECT a.*, b.* FROM Consumers a
INNER JOIN CustomerInfo b ON a.METER_NUMBER = b.METER and a.Location = b.Location
WHERE a.METER_NUMBER IS NOT NULL AND a.METER_NUMBER <> 0
UNION ALL
SELECT a.*, NULL as Meter, NULL as CustomerInfo_Location, NULL as Field2, NULL as Field3
FROM Consumers a
WHERE a.METER_NUMBER IS NULL OR a.METER_NUMBER = 0
UNION ALL
SELECT NULL as METER_NUMBER, NULL as Location, NULL as Field4, NULL as Field5, b.*
FROM CustomerInfo b
WHERE b.METER IS NULL OR b.METER = 0) c
I know to create a new table from other table you can use the following snip:
CREATE TABLE New_table
AS (SELECT customers.Meter_number, customers_info.Meter_number, ...
FROM customers, customers_info
WHERE customers.Meter_number = customers_info.Meter_number
OR customers.Meter_number IS NULL OR customers_info.Meter_number = 0);
I didn't test it out, but you should be able to do something with that.
I guess full outer join is what you need.
Create table #consumers (
meter_number int,
location varchar(50),
field4 varchar(50),
field5 varchar(50)
)
Create table #Customer_info (
meter int,
location varchar(50),
field1 varchar(50),
field2 varchar(50)
)
Insert into #consumers(meter_number ,location , field4 , field5 )
values (1234,'Dallas','a','1')
,(null, 'Denver','b','2')
,(5678,'Houston','c','3')
,(null,'Omaha','d','4')
,(0,'Portland','e','5')
,(2222,'Sacramento','f','6')
Insert into #Customer_info(meter , location )
values (1234,'Dallas')
,(null, 'Kansas')
,(5678,'Houston')
,(Null,'Denver')
,(0,'Boston')
,(4444,'NY')
Select c.*
,i.*
From #consumers c
full outer join #Customer_info i on c.meter_number=i.meter
and c.location=i.location
select * into New_Table From (select METER_NUMBER,Consumers.Location AS Location,Field4,Field5,Meter,Customer_Info.Location As Customer_Info_Location,Field2,Field3 From Consumers full outer Join Customer_Info on Consumers.METER_NUMBER=Customer_Info.Meter And Consumers.Location=Customer_Info.Location) AS t

SQL INSERT with INNER JOIN from list of constant strings

I want to create a SQL Server 2012 Query which inserts a constant list of permission names, e.g. "ViewUsersPermission", "ModifyUsersPermission" into the table RolePermissions.
This table has two columns: A "RoleID" which is a foreign key to the Roles table, and a varchar column "PermissionTypeName".
INSERT INTO [dbo].[RolePermissions] ([PermissionTypeName], [RoleID])
SELECT 'ViewUsersPermission' AS [PermissionTypeName]
UNION ALL
SELECT 'ModifyUsersPermission' AS [PermissionTypeName]
UNION ALL
SELECT 'ViewRolesPermission' AS [PermissionTypeName]
INNER JOIN (SELECT [RoleID] FROM [dbo].[Roles]
WHERE [Name] = 'Administrator')
I am looking for a better solution. I want to specify a list of permissions types like:
SELECT FROM 'ViewUsersPermission', 'ModifyUsersPermission', 'ViewRolesPermission'
instead of using the UNION ALL construct.
Assuming you are on at least 2008
SELECT RoleName
FROM (VALUES('ViewUsersPermission'),
('ModifyUsersPermission'),
('ViewRolesPermission')) V(RoleName)
Based on the help from Martin, the complete SQL INSERT statement looks the following:
INSERT INTO [dbo].[RolePermissions] ([PermissionTypeName], [RoleID])
SELECT [PermissionTypeName], [RoleID] FROM (VALUES('ViewUsersPermission'),
('ModifyUsersPermission'), ('ViewRolesPermission')) V([PermissionTypeName])
INNER JOIN [dbo].[Roles] AS [Role]
ON [Role].[Name] = 'Administrator'

Using GROUP-BY-HAVING on two joined tables

I'm trying to join two tables, and select columns from both based on where constraints as a well as a group-by-having condition. I'm experiencing some issues and behavior that I do not understand. I'm using sybase. A simple example below
CREATE TABLE #test(
name varchar(4),
num int,
cat varchar(3)
)
CREATE TABLE #other(
name varchar(4),
label varchar(20)
)
Insert #test VALUES('a',2,'aa')
Insert #test VALUES ('b',2,'aa')
Insert #test VALUES ('c',3,'bb')
Insert #test VALUES ( 'a',3,'aa')
Insert #test VALUES ( 'd',4,'aa')
Insert #other VALUES('a','this label is a')
Insert #other VALUES ('b','this label is b')
Insert #other VALUES ('c','this label is c')
Insert #other VALUES ( 'd','this label is d')
SELECT t.name,t.num,o.label
FROM #other o inner JOIN #test t ON o.name=t.name
WHERE t.name='a'
GROUP BY t.name
HAVING t.num=MAX(t.num)
I get non-sense when I have the GROUP BY (the label columns are clearly related to a different t.name). If I cut out the GROUP BY statement the query behaves as I would expect, but then I am forced to use this as a subquery and then apply
SELECT * FROM (subquery here) s GROUP BY s.name having s.num=MAX(s.num)
There has to be a better way of doing this. Any help and explanation for this behavior would be very appreciated.
**I should clarify. In my actual query I have something like
SELECT .... FROM (joined tables) WHERE name IN (long list of names), GROUP BY .....
You can try the following, if I understand your query well.
SELECT t.name,t.num,o.label
FROM #other o inner JOIN #test t ON o.name=t.name
WHERE t.name='a' AND t.num=MAX(t.num)
Your GROUP BY must include t.name, t.num and o.label. If you do this
GROUP BY t.name, t.num, o.label
then the query executes without error.
However you're not computing any aggregate values in the group by.
What are you trying to do?
If I understand your requirement correctly, running this...
SELECT t.name, num=MAX(t.num), o.label
FROM #other o
INNER JOIN #test t ON o.name=t.name
WHERE t.name='a'
GROUP BY t.name, o.label;
...gives me this result:
name num label
---- ----------- --------------------
a 3 this label is a

SQL joins with multiple records into one with a default

My 'people' table has one row per person, and that person has a division (not unique) and a company (not unique).
I need to join people to p_features, c_features, d_features on:
people.person=p_features.num_value
people.division=d_features.num_value
people.company=c_features.num_value
... in a way that if there is a record match in p_features/d_features/c_features only, it would be returned, but if it was in 2 or 3 of the tables, the most specific record would be returned.
From my test data below, for example, query for person=1 would return
'FALSE'
person 3 returns maybe, person 4 returns true, and person 9 returns default
The biggest issue is that there are 100 features and I have queries that need to return all of them in one row. My previous attempt was a function which queried on feature,num_value in each table and did a foreach, but 100 features * 4 tables meant 400 reads and it brought the database to a halt it was so slow when I loaded up a few million rows of data.
create table p_features (
num_value int8,
feature varchar(20),
feature_value varchar(128)
);
create table c_features (
num_value int8,
feature varchar(20),
feature_value varchar(128)
);
create table d_features (
num_value int8,
feature varchar(20),
feature_value varchar(128)
);
create table default_features (
feature varchar(20),
feature_value varchar(128)
);
create table people (
person int8 not null,
division int8 not null,
company int8 not null
);
insert into people values (4,5,6);
insert into people values (3,5,6);
insert into people values (1,2,6);
insert into p_features values (4,'WEARING PANTS','TRUE');
insert into c_features values (6,'WEARING PANTS','FALSE');
insert into d_features values (5,'WEARING PANTS','MAYBE');
insert into default_features values('WEARING PANTS','DEFAULT');
You need to transpose the features into rows with a ranking. Here I used a common-table expression. If your database product does not support them, you can use temporary tables to achieve the same effect.
;With RankedFeatures As
(
Select 1 As FeatureRank, P.person, PF.feature, PF.feature_value
From people As P
Join p_features As PF
On PF.num_value = P.person
Union All
Select 2, P.person, PF.feature, PF.feature_value
From people As P
Join d_features As PF
On PF.num_value = P.division
Union All
Select 3, P.person, PF.feature, PF.feature_value
From people As P
Join c_features As PF
On PF.num_value = P.company
Union All
Select 4, P.person, DF.feature, DF.feature_value
From people As P
Cross Join default_features As DF
)
, HighestRankedFeature As
(
Select Min(FeatureRank) As FeatureRank, person
From RankedFeatures
Group By person
)
Select RF.person, RF.FeatureRank, RF.feature, RF.feature_value
From people As P
Join HighestRankedFeature As HRF
On HRF.person = P.person
Join RankedFeatures As RF
On RF.FeatureRank = HRF.FeatureRank
And RF.person = P.person
Order By P.person
I don't know if I had understood very well your question, but to use JOIN, you need your table loaded already and then use the SELECT statement with INNER JOIN, LEFT JOIN or whatever you need to show.
If you post some more information, maybe turn it easy to understand.
There are some aspects of your schema I'm not understanding, like how to relate to the default_features table if there's no match in any of the specific tables. The only possible join condition is on feature, but if there's no match in the other 3 tables, there's no value to join on. So, in my example, I've hard-coded the DEFAULT since I can't think of how else to get it.
Hopefully this can get you started and if you can clarify the model a bit more, the solution can be refined.
select p.person, coalesce(pf.feature_value, df.feature_value, cf.feature_value, 'DEFAULT')
from people p
left join p_features pf
on p.person = pf.num_value
left join d_features df
on p.division = df.num_value
left join c_features cf
on p.company = cf.num_value