Need sql query for matching with three values - sql

I have a table like below
CAccountID CID NetworkID
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 1
7 3 2
8 3 3
9 4 1
10 4 2
I need a query to select all CID having all 3 NetworkID(1,2,3) and don't need to display only 1 and 2 NetworkID.
Output should be like below,
CAccountID CID NetworkID
3 2 1
4 2 2
5 2 3
6 3 1
7 3 2
8 3 3

You can use GROUP BY with JOIN :
select t.*
from table t inner join
( select cid
from table
where NetworkID in (1,2,3)
group by cid
having count(distinct NetworkID) = 3
) tt
on tt.cid = t.cid;

Try this:
select * from my_table t
where exists(select 1 from my_table
where CID = t.CID and NetworkID in (1,2,3)
group by CID
having count(*) = 3)

Try this:
select * from <<tablename>> where cid in(select cid from <<tablename>> group by cid having count(*)=3).
Here the subquery will return you all thouse cid which have 3 rows in your table.
Or if you have more network ids then use of INTERSECT operator can be helpful:
select * from <<tablename>> where cid in (
select cid from <<tablename>> where NetworkID=1
INTERSECT
select cid from <<tablename>> where NetworkID=2
INTERSECT
select cid from <<tablename>> where NetworkID=3
);
INTERSECT operator basically returns all the rows common in the queries. Thus, your data unpredicatbility can be handled in this way

Try xml path.
SELECT *
FROM Table_Name B
WHERE (SELECT [text()] = A.Network FROM Table_Name A WHERE A.CID = B.CID
ORDER BY CID, CAAccount FOR XML PATH('')) = 123
CTE Demo:
; WITH CTE(CAAccount, CID, Network) AS
(
SELECT 1 , 1, 1 UNION ALL
SELECT 2 , 1, 2 UNION ALL
SELECT 3 , 2, 1 UNION ALL
SELECT 4 , 2, 2 UNION ALL
SELECT 5 , 2, 3 UNION ALL
SELECT 6 , 3, 1 UNION ALL
SELECT 7 , 3, 2 UNION ALL
SELECT 8 , 3, 3 UNION ALL
SELECT 9 , 4, 1 UNION ALL
SELECT 10, 4, 2
) SELECT *
FROM CTE B
WHERE (SELECT [text()] = A.Network FROM CTE A WHERE A.CID = B.CID ORDER BY CID, CAAccount FOR XML PATH('')) = 123
Output:
CAAccount CID Network
3 2 1
4 2 2
5 2 3
6 3 1
7 3 2
8 3 3

Related

Recursive calculation to form a tree using sql

I am working on a simple problem and wanted to solve it using SQL. I am having 3 tables Category, Item & a relational table CategoryItem. I need to return count of items per category but the twist is Categories are arranged in Parent-Child relationships and the count of items in child categories should be added to the count in its parent Category. Please consider the sample data below and the expected resultset using SQL.
Id Name ParentCategoryId
1 Category1 Null
2 Category1.1 1
3 Category2.1 2
4 Category1.2 1
5 Category3.1 3
ID CateoryId ItemId
1 5 1
2 4 2
3 5 2
4 3 1
5 2 3
6 1 1
7 3 2
Result:
CategoryNAme Count
Category1 7
Category1.1 5
Category2.1 4
Category1.2 1
Category3.1 2
I can do it in my business layer but performance its not optimal because of size of data. I am hoping if I can do it in data layer, I would be able to improve performance greatly.
Thanks in Advance for your reply
your tables and sample data
create table #Category(Id int identity(1,1),Name Varchar(255),parentId int)
INSERT INTO #Category(Name,parentId) values
('Category1',null),('Category1.1',1),('Category2.1',2),
('Category1.2',1),('Category3.1',3)
create table #CategoryItem(Id int identity(1,1),categoryId int,itemId int)
INSERT INTO #CategoryItem(categoryId,itemId) values
(5,1),(4,2),(5,2),(3,1),(2,3),(1,1),(3,2)
create table #Item(Id int identity(1,1),Name varchar(255))
INSERT INTO #Item(Name) values('item1'),('item2'),('item3')
Checking for all childs of parent by Recursive Commom Table Expressions
;WITH CategorySearch(ID, parentId) AS
(
SELECT ID, ID AS ParentId FROM #Category
UNION ALL
SELECT CT.Id,CS.parentId FROM #Category CT
INNER JOIN CategorySearch CS ON CT.ParentId = CS.ID
)
select * from CategorySearch order by 1,2
Output: All child records against parent
ID parentId
1 1
2 1
3 1
4 1
5 1
2 2
3 2
5 2
3 3
5 3
4 4
5 5
Final query for your result, count all items for category and its children categories.
;WITH CategorySearch(ID, parentId) AS
(
SELECT ID, ID AS ParentId FROM #Category
UNION ALL
SELECT CT.Id,CS.parentId FROM #Category CT
INNER JOIN CategorySearch CS ON CT.ParentId = CS.ID
)
SELECT CA.Name AS CategoryName,count(itemId) CountItem
FROM #Category CA
INNER JOIN CategorySearch CS ON CS.ParentId = CA.id
INNER JOIN #CategoryItem MI ON MI.CategoryId =CS.ID
GROUP BY CA.Name
Output:
CategoryName CountItem
Category1 7
Category1.1 5
Category1.2 1
Category2.1 4
Category3.1 2
with help of CTE (common table expression) with recursion, you could achieve what you are looking for.
see Microsoft help for more details retated to recursive CTEs: CTE MS SQL 2008 +
hereby you could find complete example, with your sample data:
-- tables definition
SELECT 1 as id, 'cat1' as [name],NULL as id_parent
into cat
union
select 2, 'cat1.1', 1
union
select 3, 'cat2.1', 2
union
select 4, 'cat1.2', 1
union
select 5, 'cat3.1', 3
select 1 as id , 5 as id_cat, 1 as id_item
iNTO item
UNION
select 2, 4, 2
UNION
select 3, 5, 2
UNION
select 4, 3, 1
UNION
select 5, 2, 3
UNION
select 6, 1, 1
UNION
select 7, 3, 2
-- CTE to get desired result
with childs
as
(
select c.id, c.id_parent
from cat c
UNION ALL
select s.id, p.id_parent
from cat s JOIN childs p
ON (s.id_parent=p.id)
),
category_count
AS
(
SELECT c.id, c.name, count(i.id) as items
from cat c left outer join item i
on (c.id=i.id_cat)
GROUP BY c.id,c.name
),
pairs
AS
(
SELECT id, ISNULL(id_parent,id) as id_parent
FROM childs
)
select p.id_parent, n.name, sum(items)
from pairs p JOIN category_count cc
ON (p.id=cc.id)
join cat n ON (p.id_parent=n.id)
GROUP by p.id_parent ,n.name
ORDER by 1;

SQL pad query result for missing groups

Assume the following table:
TableA:
ID GroupName SomeValue
1 C 1
2 C 1
2 B 1
2 A 1
I need to construct a query that selects the following result:
ID GroupName SomeValue
1 C 1
1 B 0
1 A 0
2 C 1
2 B 1
2 A 1
The GroupName is actually derived from TableA column's CASE expression and can take only 3 values: A, B, C.
Are the analytic functions the way to go?
EDIT
Sorry, for not mentioning it, but the ID could consist of multiple columns. Consider this example:
ID1 ID2 GroupName SomeValue
1 1 C 1
1 2 C 1
2 2 C 1
2 2 B 1
2 2 A 1
I need to pad SomeValue with 0 for each unique combination ID1+ID2. So the result should be like this:
ID1 ID2 GroupName SomeValue
1 1 C 1
1 1 B 0
1 1 A 0
1 2 C 1
1 2 B 0
1 2 A 0
2 2 C 1
2 2 B 1
2 2 A 1
EDIT2
Seems like solution, proposed by #Laurence should work even for multiple-column 'ID'. I couldn't rewrite the query proposed by #Nicholas Krasnov to conform to this requirement. But could somebody compare these solutions performance-wise? Will the analytic function work faster than 'cross join + left outer join'?
To fill in gaps, you could write a similar query using partition by clause of outer join:
SQL> with t1(ID,GroupName,SomeValue) as
2 (
3 select 1, 'C', 1 from dual union all
4 select 2, 'C', 1 from dual union all
5 select 2, 'B', 1 from dual union all
6 select 2, 'A', 1 from dual
7 ),
8 groups(group_name) as(
9 select 'A' from dual union all
10 select 'B' from dual union all
11 select 'C' from dual
12 )
13 select t1.ID
14 , g.group_name
15 , nvl(SomeValue, 0) SomeValue
16 from t1
17 partition by (t1.Id)
18 right outer join groups g
19 on (t1.GroupName = g.group_name)
20 order by t1.ID asc, g.group_name desc
21 ;
ID GROUP_NAME SOMEVALUE
---------- ---------- ----------
1 C 1
1 B 0
1 A 0
2 C 1
2 B 1
2 A 1
6 rows selected
UPDATE: Response to the comment.
Specify ID2 column in the partition by clause as well:
SQL> with t1(ID1, ID2, GroupName,SomeValue) as
2 (
3 select 1, 1, 'C', 1 from dual union all
4 select 1, 2, 'C', 1 from dual union all
5 select 2, 2, 'C', 1 from dual union all
6 select 2, 2, 'B', 1 from dual union all
7 select 2, 2, 'A', 1 from dual
8 ),
9 groups(group_name) as(
10 select 'A' from dual union all
11 select 'B' from dual union all
12 select 'C' from dual
13 )
14 select t1.ID1
15 , t1.ID2
16 , g.group_name
17 , nvl(SomeValue, 0) SomeValue
18 from t1
19 partition by (t1.Id1, t1.Id2)
20 right outer join groups g
21 on (t1.GroupName = g.group_name)
22 order by t1.ID1, t1.ID2 asc , g.group_name desc
23 ;
ID1 ID2 GROUP_NAME SOMEVALUE
---------- ---------- ---------- ----------
1 1 C 1
1 1 B 0
1 1 A 0
1 2 C 1
1 2 B 0
1 2 A 0
2 2 C 1
2 2 B 1
2 2 A 1
9 rows selected
Select
i.Id1,
i.Id2,
g.GroupName,
Coalesce(a.SomeValue, 0) As SomeValue
From
(select distinct ID1, ID2 from TableA) as i
cross join
(select distinct GroupName from TableA) as g
left outer join
tableA a
on i.ID = a.ID and g.GroupName = a.GroupName
Order By
1,
2,
3 Desc

sql server : count records

I have a tableA (ID int, Match varchar, tot int)
ID Match Tot
1 123
2 123
3 12
4 12
5 4
6 12
7 8
Now, I want to calculate Tot which is total number of match exists in the table. for example 123 occured twice, 12 exist thrice and so on. Also note that I want the count only at first match. here is the expected result.:
ID Match Tot
1 123 2
2 123
3 12 3
4 12
5 4 1
6 12
7 8 1
Another case:
ID Match Count Tot
1 123 2
2 123 1
3 12 10
4 12 10
5 4 3
6 12 5
7 8 7
Now I want to add the count for the same match. expected result:
ID Match Count Tot
1 123 2 3
2 123 1
3 12 10 25
4 12 10
5 4 3 3
6 12 5
7 8 7 7
Thanks
WITH tableA(ID, Match) AS
(
SELECT 1,123 UNION ALL
SELECT 2,123 UNION ALL
SELECT 3,12 UNION ALL
SELECT 4,12 UNION ALL
SELECT 5,4 UNION ALL
SELECT 6,12 UNION ALL
SELECT 7,8
)
SELECT *,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Match ORDER BY ID) = 1
THEN COUNT(*) OVER (PARTITION BY Match)
END AS Tot
FROM tableA
ORDER BY ID
SELECT match, COUNT(match ) as Tot
FROM tableA
GROUP BY match
Solution 1:
DECLARE #MyTable TABLE
(
ID INT PRIMARY KEY
,Match VARCHAR(10) NOT NULL
,Tot INT NULL
);
INSERT #MyTable(ID, Match)
SELECT 1, 123
UNION ALL
SELECT 2, 123
UNION ALL
SELECT 3, 12
UNION ALL
SELECT 4, 12
UNION ALL
SELECT 5, 4
UNION ALL
SELECT 6, 12
UNION ALL
SELECT 7, 8;
--SELECT
SELECT *
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a;
--UPDATE
WITH MyCTE
AS
(
SELECT a.Tot
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a
)
UPDATE MyCTE
SET Tot = TotCalculated;
SELECT *
FROM #MyTable;
Solution 2:
UPDATE #MyTable
SET Tot = NULL;
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
ORDER BY x.ID
UPDATE #MyTable
SET Tot = t.Num
FROM #MyTable z
INNER JOIN
(
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
) t ON z.ID = t.ID;
SELECT *
FROM #MyTable;

How to get the max version records?

I have a table like the following:
------------------------------------
Id FId UId Version
1 1 1 1
2 1 2 1
3 1 3 1
4 1 2 2
5 1 3 2
6 1 3 2
7 1 4 2
8 2 1 1
9 2 2 1
then I want the result to be:
--------------------------
FId UId Version
1 2 2
1 3 2
1 4 2
2 1 1
2 2 1
How to write the query based on the max 'Version' of each FId-UId pair?
The following gives the output requested.
select distinct t2.FId, t2.UId, t2.Version
from
(
select FId, max(Version) as "Version"
from MyTable
group by FId
) t1
inner join MyTable t2 on (t1.FId = t2.FId and t1.Version = t2.Version)
order by t2.FId, t2.UId
This will work on SQL 2005 and later:
DECLARE #t TABLE
(Id INT,
Fid INT,
[uid] INT,
[VERSION] INT
)
INSERT #t
SELECT 1,1,1,1
UNION ALL SELECT 2,1,2,1
UNION ALL SELECT 3,1,3,1
UNION ALL SELECT 4,1,2,2
UNION ALL SELECT 5,1,3,2
UNION ALL SELECT 6,1,3,2
UNION ALL SELECT 7,1,4,2
UNION ALL SELECT 8,2,1,1
UNION ALL SELECT 9,2,2,1
;WITH myCTE
AS
(
SELECT *,
RANK() OVER (PARTITION BY Fid
ORDER BY [VERSION] DESC
) AS rnk
FROM #t
)
SELECT DISTINCT Fid, [uid],[VERSION]
FROM myCTE
WHERE rnk = 1
ORDER BY Fid, [uid]
select FId, UId, Version
from MyTable
join (select Fid, Max(Version) as MaxVersion group by Fid) x
on x.FId = MyTable.FId and x.MaxVersion = MyTable.Version
Is the result you show correct- 1,3,2 should appear twice.If you need only once use select distinct
The foll query is working
with t as(
select 1 as id, 1 as fid , 1 as uid, 1 as version union all
select 2 , 1 , 2 , 1 union all
select 3 , 1 , 3 , 1 union all
select 4 , 1 , 2 , 2 union all
select 5 , 1 , 3 , 2 union all
select 6 , 1 , 3 , 2 union all
select 7 , 1 , 4 , 2 union all
select 8 , 2 , 1 , 1 union all
select 9 , 2 , 2 , 1)
select distinct t.fid,t.uid,t.version from t
inner join(
select fid,max(version) as maxversion from t
group by fid)as grp
on t.fid=grp.fid
and t.version=grp.maxversion

to frame cost effective query

Could you please help me out in building a Query. I Have a table as below
Id Info_Id Type
1 2 2
2 6 2
3 5 3
4 8 3
5 2 3
6 2 2
7 5 2
8 8 2
9 5 2
10 8 2
11 8 2
12 5 3
13 6 3
14 8 3
a query need to be framed so as to group by "Info_Id".
I need output as below for eg:
Info_Id CountOfRec Type2 Type3
2 3 2 1
5 4 2 2
6 2 1 1
8 5 3 2
I Tried as below but I m not able to get the efficient output
select Info_Id, count(Id)as CountOfRec,
(select count(Id)from tbl_TypeInfo where Info_Id = 5 AND Type = 2) as Type2,
(select count(Id)from tbl_ TypeInfo where Info_Id = 5 AND Type = 3) as Type3
from tbl_TypeInfo
where Info_Id = 5
group by Info_Id
output was this ,
Info_Id CountOfRec Type2 Type3
5 4 2 2
(I have to loop for each “Info_id” to get desired OP, there is thousand of records and its time consuming)
I wanted to get the highlighted output from the table. The query I have framed is not efficient and there would be good solution for this can you help me out.
You can use the CASE expression to only count the rows for a specific type:
SELECT Info_Id,
COUNT(*) AS CountOfRec,
COUNT(CASE WHEN Type = 2 THEN 1 ELSE NULL END) AS Type2
COUNT(CASE WHEN Type = 3 THEN 1 ELSE NULL END) AS Type3
FROM tbl_TypeInfo
GROUP BY Info_Id
Add a WHERE Info_Id = 5 to retrieve results for a specific ID only.
Update: as per comments, if you do not store a table of ID's, You need to change your IN(..) list to a virtual "table":
SELECT vt.id,
COUNT(*) AS CountOfRec,
COUNT(CASE WHEN Type = 2 THEN 1 ELSE NULL END) AS Type2,
COUNT(CASE WHEN Type = 3 THEN 1 ELSE NULL END) AS Type3
FROM (
SELECT 1 id
UNION SELECT 2
UNION SELECT 3
UNION SELECT 5
UNION SELECT 8
) AS vt LEFT JOIN tbl_TypeInfo ON vt.id = tbl_TypeInfo.Info_Id
GROUP BY vt.id
You can use SQL Server's PIVOT operator
SELECT Info_ID
, CountOfRec = [2] + [3]
, Type2 = [2]
, Type3 = [3]
FROM (
SELECT *
FROM (
SELECT *
FROM tbl_TypeInfo
) s
PIVOT (COUNT(Id) FOR Type IN ([2], [3])) pvt
) q
Test
;WITH tbl_TypeInfo AS (
SELECT [Id] = 1, [Info_Id] = 2, [Type] = 2
UNION ALL SELECT 2, 6, 2
UNION ALL SELECT 3, 5, 3
UNION ALL SELECT 4, 8, 3
UNION ALL SELECT 5, 2, 3
UNION ALL SELECT 6, 2, 2
UNION ALL SELECT 7, 5, 2
UNION ALL SELECT 8, 8, 2
UNION ALL SELECT 9, 5, 2
UNION ALL SELECT 1, 8, 2
UNION ALL SELECT 1, 8, 2
UNION ALL SELECT 1, 5, 3
UNION ALL SELECT 1, 6, 3
UNION ALL SELECT 1, 8, 3
)
SELECT Info_ID
, CountOfRec = [2] + [3]
, Type2 = [2]
, Type3 = [3]
FROM (
SELECT *
FROM (
SELECT *
FROM tbl_TypeInfo
) s
PIVOT (COUNT(Id) FOR Type IN ([2], [3])) pvt
) q
This is a bit crazy, but if the types are always, always 2 and 3, this could be threated as an equation, where count(type2)+count(type3)=count(*) and 2*count(type2)+3*count(type3)=sum(*) so you could to something like
SELECT 3*c-s as Type2Count, s-2*c as Type3Count
FROM (SELECT COUNT(*) as C, SUM(Type) as S
FROM tbl_TypeInfo
WHERE Info_Id = 5) SourceTable
This will be lightning fast, however, this is extemely breakable!!!!
If ever, the types are changed, or a type is added, this will not work.