LINQ adding columns to base result set - c#

I have three classes, Code,CodeContact and Contact which map to tables in a database. In my code the classes have one static method which returns the data you'll find in the following SQL script
create table #Contacts
(ContactId int
,Name varchar(50)
,Phone varchar(50)
)
insert into #Contacts(ContactId,Name,Phone)
values (1,'jon',111 )
,(2,'jim',222)
,(3,'james',333)
,(4,'john',444)
,(5,'tim',555)
,(6,'jane',666)
go
create table #ContactsCodes
(ContactId int
,CodeId int
)
insert into #ContactsCodes(ContactId,CodeId)
values (1,1 )
,(1,2 )
,(1,3 )
,(1,4 )
,(2,2 )
,(2,3 )
,(3,1 )
,(3,4 )
,(4,1 )
,(4,5 )
create table #Codes
(CodeId int
,CodeText varchar(50)
)
insert into #Codes
values (1,'Code Red' )
,(2,'Code Blue' )
,(3,'Code Green' )
,(4,'Code Gray' )
,(5,'Code Black' )
,(6,'code clear' )
I'm looking for some advice on how to get LINQ code to acieve something like that following
declare #filter varchar(50) = (select 1)
select c.ContactId
,c.Name
,c.Phone
,case when varColumn.ContactId is null
then 'Not Present'
else 'Present' end as [Code Red]
,case when varColumn2.ContactId is null
then 'Not Present'
else 'Present' end as [Code Blue]
from Contacts as c
--****************end of 'cohort' base set of IDs
--first item in filter list becomes a
--column on a DataTable
left join
(
select distinct cc.ContactId
from Codes as codes
left join ContactsCodes as cc
on cc.CodeId = codes.CodeId
where codes.CodeId =#filter
) as varColumn on varColumn.ContactId = c.ContactId
--second item in filter becomes another column
left join
(
select distinct cc.ContactId
from Codes as codes
left join ContactsCodes as cc
on cc.CodeId = codes.CodeId
where codes.CodeId =#filter+1
) as varColumn2 on varColumn2.ContactId = c.ContactId
So let's say I have an IEnumerable with 1,2,3 in it. I would like to iterate over the collection and left join to the Contact.ContactId column and simply give an indication if that particular CodeId was found in the ContactsCodes table. Here's what the result looks like if you run the above SQL:
I will eventually need the tackd on column data to be pretty dynamic. But for the time being, can anyone provide a direction to go? I think the easiest choice might be to go the LINQ to SQL route and have the stuff inside the left join a stored procedure and call it X number of times for whatever's in the list, but I'm welcome to any suggestions.

Related

DISTINCT query not working its give me repeated columns on output

Buddy
i have one query of MSSQL.
that is like this..
SELECT DISTINCT resource.locationurl,
resource.resourcename,
resource.anwserid,
checktotal.total
FROM resource
INNER JOIN (SELECT Count(DISTINCT anwserid) AS total,
resourcename
FROM resource AS Resource_1
WHERE ( anwserid IN (SELECT Cast(value AS INT) AS Expr1
FROM dbo.Udf_split(#sCategoryID, ',')
AS
udf_Split_1) )
GROUP BY resourcename) AS checktotal
ON resource.resourcename = checktotal.resourcename
WHERE ( resource.anwserid IN (SELECT Cast(value AS INT) AS Expr1
FROM dbo.Udf_split(#sCategoryID, ',') AS
udf_Split_1)
)
AND ( checktotal.total = #Total )
ORDER BY resource.resourcename
I run this query but its give me repeated column of Resource.LocationURL.
you can check it live hear http://www.ite.org/visionzero/toolbox/default2.aspx
check in above link where you can fire select some category but result was not distinct..
i try most of my but now i am out of mind please help me with this.
You misunderstand what DISTINCT means when you are fetching more than one column.
If you run this query:
SELECT DISTINCT col1, col2 FROM table
You are selected every different combination. An acceptable result would be
value 1_1, value 2_1
value 1_1, value 2_2,
value 2_1, value_2_1
In this example, value 1_1 appears twice, but the two columns combined are unique.
My guess is that you are actually attempting to perform a grouping:
SELECT resource.locationurl,
resource.resourcename,
resource.anwserid,
Sum(checktotal.total)
FROM resource
INNER JOIN (SELECT Count(DISTINCT anwserid) AS total,
resourcename
FROM resource AS Resource_1
WHERE ( anwserid IN (SELECT Cast(value AS INT) AS Expr1
FROM dbo.Udf_split(#sCategoryID, ',')
AS
udf_Split_1) )
GROUP BY resourcename) AS checktotal
ON resource.resourcename = checktotal.resourcename
WHERE ( resource.anwserid IN (SELECT Cast(value AS INT) AS Expr1
FROM dbo.Udf_split(#sCategoryID, ',') AS
udf_Split_1)
)
AND ( checktotal.total = #Total )
GROUP BY resource.locationurl,
resource.resourcename,
resource.anwserid
First of all, the site you linked doesn't do anything.
Second, DISTINCTensures unique rows. It will not make the values in all the columns unique as well. Just think about it! How would it work? You have two rows with the same locationurl field, but with otherwise distinct elements. Which one do you not include?
Lastly, please take greater care in phrasing your questions.
as I see your query is select DISTINCT on multi columns,
so if any record has at least one col difference then it pass the DISTINCT condition
Ex:
record1 : locationurl | resourcename | anwserid | Sum(checktotal.total)
loc1 res1 1 100
record2 : locationurl | resourcename | anwserid | Sum(checktotal.total)
loc1 res1 2 100

How to do Searching and Filtering Data based on Checkboxes in Many to Many Relationship in SQL?

Basically i have 3 tables like this (with many to many relationship);
And i am querying searching like this;
ALTER PROC [dbo].[usp_ContactSearch]
(
#PersonName varchar(60)= '',
#MobileNo varchar(20)= '',
#Nationlity varchar(50)='' ,
#ContactTypes varchar(max) = ''
)
AS
BEGIN
SELECT DISTINCT c.ContactId, c.PersonName, ct.ContactType, ct.ContactTypeId
FROM Contact c
LEFT OUTER JOIN ContactWithContactType cct
ON c.ContactId = cct.ContactId
LEFT OUTER JOIN ContactType ct
ON cct.CountactTypeId = ct.ContactTypeId
WHERE
c.PersonName LIKE CASE WHEN #PersonName='' THEN c.PersonName ELSE '%'+#PersonName+'%' END
AND
c.MobileNo1 LIKE CASE WHEN #MobileNo='' THEN c.MobileNo1 ELSE '%'+#MobileNo+'%' END
AND
c.Nationality LIKE CASE WHEN #Nationlity='' THEN c.Nationality ELSE '%'+#Nationlity+'%' END
END
So, the result data by default is;
So, from the Front End, i have ContactTypes (which are dynamic i.e comming from contact types table), and the interface looks like this
Now, whenever user check the PropertyOwner(ContactTypeId=1), The data should be filtered and only those contacts should be shown which are belong to ContactTypeId=1
Silarly, when i check the second checkbox i.e Tenant(ContactTypeId=2). The data should be more filtered and only those contacts will be displayed which belongs to ContactTypeId= 1 and 2. And similarly for 3rd ContactType, the data should be more filtered and so on and so forth.
So, the problem is ContactTypes are dynamic and i don't know how to handle this situation.
Any help regards to the query and performance is much apreciated.
Try this. This will work...
-- This is User Defined Table Type Variable
Declare #MyTypeDataType ContType
-- You will pass value to this variable from Front End
Insert into #MyTypeDataType values(1),(2),(3);
-- From Front end you will pass the
-- selected values to "Table Type Variable" and
-- also to a "Varchar" Variable
Declare #Type as Varchar(20);
SET #Type = '1,2,3';
SELECT X.* FROM
(
-- This query will get all persons,
-- who have any one Type u want to Search...
SELECT C.*,CTT.ContactType, CTT.ContactTypeId FROM Contact C
INNER JOIN ContactWithType CT
ON C.ContactId = CT.ContactId
INNER JOIN ContactType CTT
ON CTT.ContactTypeId = CT.ContactTypeId
WHERE #Type LIKE '%' + CAST( CT.ContactTypeId AS VARCHAR(MAX)) + '%'
) X
INNER JOIN
(
-- This will count the Record of each Person,
-- how many time a persons record exists..
SELECT C.ContactId, COUNT(C.ContactId) AS Total
FROM Contact C
INNER JOIN ContactWithType CT
ON C.ContactId = CT.ContactId
INNER JOIN ContactType CTT
ON CTT.ContactTypeId = CT.ContactTypeId
WHERE #Type LIKE '%' + CAST( CT.ContactTypeId AS VARCHAR(MAX)) + '%'
GROUP BY C.ContactId
)Y
ON X.ContactId = Y.ContactId
-- Filter persons
AND Y.Total = (SELECT COUNT(*) FROM #MyTypeDataType)
I would like to recommend to use split function for your query to filter ContactTypes. For example, when in your form someone check Property Owner and Tenant (contactType = 1, 2) or so on, then you can pass it to your stored proc as such
#ContactTypes varchar(max) = '1,2'
Then you can use one of the split string function that you need to create. You can refer to this excellent article (http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings).
I use the article function SplitStrings_Moden for simple string split.
Then you can use it like in your stored proc like this.
And ContactType in (select [item] from SplitStrings_Moden(#ContactTypes , ','))
As for performance wise, the example given return a table of string column which in your case, you could cast it to int for better for performance. But i guess you might to test it on your dataset if the performance is reasonable without the casting.
I would recommend using xml datatype for this. You can find more information here.
Use XML as input variable to an SP

convert xml string in a sql table to dynamic columns

I have two tables (using table variables for illustration. You can run these directly in management studio) that are related by the Id column.
Items in the first table has some standard set of columns and the second table has some extended parameter data for the same record. I'm storing the extended set as xml as it is dynamic in all aspects (different per product or new values being added etc).
I'm able to join these two tables and flatten out the column list as you can see in the example below. But my query requires to have the dynamic columns be defined beforehand. I would like to have this truly dynamic in the sense that if I were to add a new column in the #extended table, it should automatically come out as a new column in the output column list.
Basically the list of additional columns should be determined by the xml for that record. column name should be the xml tag and value should be value for the xml tag for each id.
Any pointers? (and can it be fast too with around 100k records or more in each table)
declare #standard table
(
Id INT,
Column1 varchar(10),
Column2 varchar(10),
Column3 varchar(10)
)
declare #extended table
(
Id INT,
column1 xml
)
insert into #standard values (1,'11', '12', '13')
insert into #standard values (2,'21', '22', '23')
insert into #extended values (1,'<FieldSet><Field><id>1</id><column4>1x</column4><column5>4x</column5></Field></FieldSet>')
insert into #extended values (2,'<FieldSet><Field><id>2</id><column4>2x</column4><column5>5x</column5></Field></FieldSet>')
select s.column1, s.column2,
(
SELECT Item2.value('(column4)[1]', 'varchar(50)')
FROM
e.column1.nodes('/FieldSet') AS T(Item)
CROSS APPLY e.column1.nodes('/FieldSet/Field') AS T2(Item2)
) column4,
(
SELECT Item2.value('(column5)[1]', 'varchar(50)')
FROM
e.column1.nodes('/FieldSet') AS T(Item)
CROSS APPLY e.column1.nodes('/FieldSet/Field') AS T2(Item2)
) column5
from #extended e
join #standard s on s.Id = e.Id
First off you can simplify your current query a bit.
select s.column1,
s.column2,
e.column1.value('(/FieldSet/Field/column4)[1]', 'varchar(50)') as column4,
e.column1.value('(/FieldSet/Field/column5)[1]', 'varchar(50)') as column5
from extended as e
join standard as s
on s.Id = e.Id
To do what you want will not be easy or fast. You need to get a list of all name/value pairs in your XML.
select T1.X.value('.', 'int') as Id,
T2.X.value('local-name(.)', 'sysname') as Name,
T2.X.value('.', 'varchar(10)') as Value
from extended as e
cross apply e.column1.nodes('/FieldSet/Field/id') as T1(X)
cross apply e.column1.nodes('/FieldSet/Field/*[position() > 1]') as T2(X)
Use that in a pivot query and join to standard.
select S.column1,
S.column2,
P.column4,
P.column5
from standard as s
inner join
(
select id, P.column4, P.column5
from (
select T1.X.value('.', 'int') as Id,
T2.X.value('local-name(.)', 'sysname') as Name,
T2.X.value('.', 'varchar(10)') as Value
from extended as e
cross apply e.column1.nodes('/FieldSet/Field/id') as T1(X)
cross apply e.column1.nodes('/FieldSet/Field/*[position() > 1]') as T2(X)
) as e
pivot (min(Value) for Name in (column4, column5)) P
) P
on S.Id = P.Id
To do this with a dynamic number of columns returned you need to build this pivot query dynamically.
Store the Name/Value pairs in temp table, use that table to figure out the columns you need and to build your query.
create table #ext
(
Id int,
Name sysname,
Value varchar(10),
primary key(Id, Name)
)
insert into #ext(Id, Name, Value)
select T1.X.value('.', 'int') as Id,
T2.X.value('local-name(.)', 'sysname') as Name,
T2.X.value('.', 'varchar(10)') as Value
from extended as e
cross apply e.column1.nodes('/FieldSet/Field/id') as T1(X)
cross apply e.column1.nodes('/FieldSet/Field/*[position() > 1]') as T2(X)
declare #SQL nvarchar(max)
set #SQL =
'select S.column1,
S.column2,
[COLLIST]
from standard as s
inner join
(
select id, [COLLIST]
from #ext as e
pivot (min(Value) for Name in ([COLLIST])) P
) P
on S.Id = P.Id'
declare #ColList nvarchar(max)
set #ColList =
(select ','+Name
from #ext
group by Name
for xml path(''), type).value('.', 'nvarchar(max)')
set #SQL = replace(#SQL, '[COLLIST]', stuff(#ColList, 1, 1, ''))
exec (#SQL)
drop table #ext
I hope it will help you
SELECT #COUNT_XML=0
SELECT #COUNT_XML=(SELECT #xxxxx_GROUP_ID.value('count(/NewDataSet/position/ID)', 'INT'))
IF(#COUNT_XML > 0)
BEGIN
IF OBJECT_ID('tempdb..#TBL_TEMPOSITION') IS NOT NULL
DROP TABLE #TBL_TEMPOSITION
CREATE TABLE #TBL_TEMPOSITION (ID NUMERIC(18,0))
INSERT INTO #TBL_TEMPOSITION (ID)
SELECT XMLxxxxGroup.PositionGPItem.value('.','NUMERIC(18,0)')
FROM #xxxxx_GROUP_ID.nodes('/NewDataSet/position/ID') AS XMLPositionGroup(PositionGPItem)
SELECT #emp_cond =#emp_cond+ N' AND CM.STATIC_EMP_INFO.POSITION_ID IN (SELECT ID FROM #TBL_TEMPOSITION) '
END

SQL insert into not exists on multiple results

I have Plan table that has a one to many relationship with a Price table.
(1 plan can have many prices)
However the problem is I have another table called "StandardPrices" that holds the names of all the prices (The reason for this is I want to be able to add or remove prices at any time)
Plan Table:
ID int primary key,
plan Name varchar(200),
...
PriceTable:
ID int primary key,
PlanId int foreign key references plan(ID)
PriceName ID foreign key standardprices(id)
StandardPrices:
ID int primary key,
PriceName varchar(200),
DefaultPrice money
So whenever a plan is created, it automatically creates a List of all the prices in the StandardPrice list (with default values).
The problem I have, Is I need, whenever I create a new StandardPrice, it automatically checks if that price exists in every plan, and if it doesnt, create an entry in the price table for that planid.
I use Stored procedures and thought the best way to do this would be through SQL.
When StandardPrices are created:
begin
insert into StandardPrices (PriceName, Defaultprice)
values (#priceName, #DefaultPrice)
end
begin
//list all plans.
//cross reference PriceTable to see if plan exists
//if not insert priceplan with default value
end
I am a bit confused how i can implement such a sql command?
I think it looks something like this:
insert into PriceTable (PlanId, PriceName)
select PlanId, #priceName
from Plan
where not exists
(select null from PriceTable where PriceTable.PlanId = Plan.PlanId)
And you should probably do this as part of an INSERT trigger on your database.
Do something like this:
if not exists
(
select *
from PriceTable
where PriceName = #priceName
)
begin
insert into PriceTable(PriceName)
values(#priceName)
end
What this does is it conditionally checks to see if that PriceName is already in the PriceTable. If it is not, then it will insert that new #priceName into the PriceTable. I wasn't sure by your original post what the value would be for PlanID, but you should get the idea from my above query.
You need to get a list of all plans, LEFT OUTER joined to the appropriate price. If the price record is null, there isn't an appropriate association so add it.
You'll probably need to use a cursor to iterate through the results and add prices.
Something like..
SELECT Plan.Id, Price.Id
FROM Plan LEFT OUTER JOIN Price ON Price.PlanId = Plan.Id
WHERE Price.Id = [Your New Price Id]
Using MERGE and assuming your ID columns have the IDENTITY property:
MERGE INTO PriceTable
USING
(
SELECT p1.ID AS PlanId, sp1.PriceName
FROM Plan p1
CROSS JOIN StandardPrices sp1
) AS SourceTable
ON PriceTable.PlanId = SourceTable.PlanId
AND PriceTable.PriceName = SourceTable.PriceName
WHEN NOT MATCHED
THEN
INSERT ( PlanId, PriceName )
VALUES ( PlanId, PriceName ) ;
Here is a fuller sketch, where I've drooped the seemingly redundant ID columns, promoted the name columns to relational keys, added constraints, used consistent naming between tables, changed MONET to DECIMAL, etc:
CREATE TABLE Plans
(
plan_name VARCHAR(200) NOT NULL
UNIQUE
CHECK ( plan_name <> ' ' )
) ;
CREATE TABLE StandardPrices
(
price_name VARCHAR(200) NOT NULL
UNIQUE
CHECK ( price_name <> ' ' ),
default_price DECIMAL(19, 4) NOT NULL
CHECK ( default_price >= 0 )
) ;
CREATE TABLE Prices
(
plan_name VARCHAR(200) NOT NULL
REFERENCES Plans ( plan_name ),
price_name VARCHAR(200) NOT NULL
REFERENCES StandardPrices ( price_name ),
UNIQUE ( plan_name, price_name )
) ;
DECLARE #plan_name_new VARCHAR(200) ;
DECLARE #price_name_1 VARCHAR(200) ;
DECLARE #default_price_1 DECIMAL(19, 4) ;
DECLARE #price_name_2 VARCHAR(200) ;
DECLARE #default_price_2 DECIMAL(19, 4) ;
SET #plan_name_new = 'One'
SET #price_name_1 = 'Day'
SET #default_price_1 = 55 ;
SET #price_name_2 = 'When'
SET #default_price_2 = 99
INSERT INTO Plans ( plan_name )
VALUES ( #plan_name_new ) ;
INSERT INTO StandardPrices ( price_name, default_price )
VALUES ( #price_name_1, #default_price_1 ) ;
INSERT INTO StandardPrices ( price_name, default_price )
VALUES ( #price_name_2, #default_price_2 ) ;
MERGE INTO Prices
USING
(
SELECT p1.plan_name, sp1.price_name
FROM Plans p1
CROSS JOIN StandardPrices sp1
) AS SourceTable
ON Prices.plan_name = SourceTable.plan_name
AND Prices.price_name = SourceTable.price_name
WHEN NOT MATCHED
THEN
INSERT ( plan_name, price_name )
VALUES ( plan_name, price_name ) ;
(MySql) I'll suggest you to create with temporary table; It is clean and readable (I will add without alias to be more readable):
CREATE TEMPORARY TABLE tempTable
(
table1FK varchar(250),
table2Value varchar(250)
);
INSERT INTO tempTable(table1FK, table2Value)
VALUES ('it', '+39'),
('lt', '+370'),
('gb', '+44'),
('cn', '+86');
INSERT INTO table2(table1FK_id, table2Value)
SELECT table1.id, tempTable.prefix
FROM tempTable
inner join table1 ON table1.code = tempTable.country_code
left join table2 ON table2.country_id = table1.id
where table2.id is null;
DROP temporary TABLE tempTable;

Suggestion for a tag cloud algorithm

I have a MSSQL 2005 table:
[Companies](
[CompanyID] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](128),
[Description] [nvarchar](256),
[Keywords] [nvarchar](256)
)
I want to generate a tag cloud for this companies. But I've saved all keywords in one column separated by commas. Any suggestions for how to generate tag cloud by most used keywords. There could be millions of companies approx ten keywords per company.
Thank you.
Step 1: separate the keywords into a proper relation (table).
CREATE TABLE Keywords (KeywordID int IDENTITY(1,1) NOT NULL
, Keyword NVARCHAR(256)
, constraint KeywordsPK primary key (KeywordID)
, constraint KeywordsUnique unique (Keyword));
Step 2: Map the many-to-many relation between companies and tags into a separate table, like all many-to-many relations:
CREATE TABLE CompanyKeywords (
CompanyID int not null
, KeywordID int not null
, constraint CompanyKeywords primary key (KeywordID, CompanyID)
, constraint CompanyKeyword_FK_Companies
foreign key (CompanyID)
references Companies(CompanyID)
, constraint CompanyKeyword_FK_Keywords
foreign key (KeywordID)
references Keywords (KeywordID));
Step 3: Use a simple GROUP BY query to generate the 'cloud' (by example taking the 'cloud' to mean the most common 100 tags):
with cte as (
SELECT TOP 100 KeywordID, count(*) as Count
FROM CompanyKeywords
group by KeywordID
order by count(*) desc)
select k.Keyword, c.Count
from cte c
join Keyword k on c.KeywordID = k.KeywordID;
Step 4: cache the result as it changes seldom and it computes expensively.
I'd much rather see your design normalized as suggested by Remus, but if you're at a point where you can't change your design...
You can use a parsing function (the example I'll use is taken from here), to parse your keywords and count them.
CREATE FUNCTION [dbo].[fnParseStringTSQL] (#string NVARCHAR(MAX),#separator NCHAR(1))
RETURNS #parsedString TABLE (string NVARCHAR(MAX))
AS
BEGIN
DECLARE #position int
SET #position = 1
SET #string = #string + #separator
WHILE charindex(#separator,#string,#position) <> 0
BEGIN
INSERT into #parsedString
SELECT substring(#string, #position, charindex(#separator,#string,#position) - #position)
SET #position = charindex(#separator,#string,#position) + 1
END
RETURN
END
go
create table MyTest (
id int identity,
keywords nvarchar(256)
)
insert into MyTest
(keywords)
select 'sql server,oracle,db2'
union
select 'sql server,oracle'
union
select 'sql server'
select k.string, COUNT(*) as count
from MyTest mt
cross apply dbo.fnParseStringTSQL(mt.keywords,',') k
group by k.string
order by count desc
drop function dbo.fnParseStringTSQL
drop table MyTest
Both Remus and Joe are correct but yes as what Joe said if you dont have a choice then you have to live with it. I think I can offer you an easy solution by using an XML Data Type. You can already easily view the parsed column by doing this query
WITH myCommonTblExp AS (
SELECT CompanyID,
CAST('<I>' + REPLACE(Keywords, ',', '</I><I>') + '</I>' AS XML) AS Keywords
FROM Companies
)
SELECT CompanyID, RTRIM(LTRIM(ExtractedCompanyCode.X.value('.', 'VARCHAR(256)'))) AS Keywords
FROM myCommonTblExp
CROSS APPLY Keywords.nodes('//I') ExtractedCompanyCode(X)
now knowing that you can do that, all you have to do is to group them and count, but you cannot group XML methods so my suggestion is create a view of the query above
CREATE VIEW [dbo].[DissectedKeywords]
AS
WITH myCommonTblExp AS (
SELECT
CAST('<I>' + REPLACE(Keywords, ',', '</I><I>') + '</I>' AS XML) AS Keywords
FROM Companies
)
SELECT RTRIM(LTRIM(ExtractedCompanyCode.X.value('.', 'VARCHAR(256)'))) AS Keywords
FROM myCommonTblExp
CROSS APPLY Keywords.nodes('//I') ExtractedCompanyCode(X)
GO
and perform your count on that view
SELECT Keywords, COUNT(*) AS KeyWordCount FROM DissectedKeywords
GROUP BY Keywords
ORDER BY Keywords
Anyways here is the full article -->http://anyrest.wordpress.com/2010/08/13/converting-parsing-delimited-string-column-in-sql-to-rows/

Categories