SQL - ALL, Including all values - sql

I have two tables:
create table xyz
(campaign_id varchar(10)
,account_number varchar)
Insert into xyz
values ( 'A', '1'), ('A', '5'), ('A', '7'), ('A', '9'), ('A', '10'),
( 'B', '2'), ('B', '3'),
( 'C', '1'), ('C', '2'), ('C', '3'), ('C', '5'), ('C', '13'), ('C', '15'),
('D', '2'), ('D', '9'), ('D', '10')
create table abc
(account_number varchar)
insert into abc
values ('1'), ('2'), ('3'), ('5')
Now, I want to write a query where all the four account_number 1, 2, 3, 5 are included in a Campaign_id.
The answer is C.
[My aim is to find the Campaign Code that includes account_number 1, 2, 3 & 5. This condition is only satisfied by campaign code C.]
I tried using IN and ALL, but don't work. Could you please help.

I think what you are after is a inner join. Not sure from your questions which way around you want your data. However this should give you a good clue how to procede and what keywords to lock for in the documentation to go further.
SELECT a.*
FROM xyz a
INNER JOIN abc b ON b.account_number = a.account_number;
EDIT:
Seems I misunderstood the original question.. sorry. To get what you want you can just do:
SELECT campaign_id
FROM xyz
WHERE account_number IN ('1', '2', '3', '5')
GROUP BY campaign_id
HAVING COUNT(DISTINCT account_number) = 4;
This is called relational division if you want to investigate further.

SELECT campaign_id
FROM (
SELECT campaign_id, COUNT(*) AS c, total_accounts
FROM xyz
JOIN abc ON xyz.account_number = abc.account_number
CROSS JOIN (SELECT COUNT(*) AS total_accounts
FROM abc) AS x
GROUP BY campaign_id
HAVING c = total_accounts) AS subq
DEMO

select xyz.campaign_id
from xyz
join abc
on xyz.account_number = abc.account_number
group by xyz.campaign_id
having count(xyz.campaign_id) =
(select count(account_number) from abc);
Caution: t-sql implementation

Related

Select based on group membership

Suppose I have two tables t and o. I want to select all the rows of t in which t.feature = o.feature (ergo first two rows).
In addition, I also want to select all the rows whose t.grouping = t.grouping of the previously selected rows. In this case t.grouping 1, 2 ergo rows ('E', 1), ('F', 2) and ('G', 1). What is the most elegant way of achieving this?
CREATE TABLE t
( feature VARCHAR,
grouping BIGINT
);
INSERT INTO t (feature, grouping)
VALUES ('A', 1),
('B', 2),
('C', 5),
('D', 4),
('E', 1),
('F', 2),
('G', 1);
CREATE TABLE o
( feature VARCHAR
);
INSERT INTO o (feature)
VALUES ('A'),
('B');
If I understood correctly try this...
with cte as
(select grouping from t inner join o on t.feature=o.feature)
select t.*
from t
inner join cte on t.grouping=cte.grouping
or
select
*
from t
where grouping in
(select grouping from t inner join o on t.feature=o.feature )
DEMO

Transpose many aggregates in SQL

I have two tables, cases, my main table, and activities, which shows work being done against certain cases.
CREATE TABLE cases
([caseno] int, [case_detail] varchar(8), [date_received] datetime)
;
INSERT INTO cases
([caseno], [case_detail], [date_received])
VALUES
(1, 'DETAIL A', '2018-04-01 00:00:00'),
(2, 'DETAIL B', '2018-05-01 00:00:00'),
(3, 'DETAIL C', '2018-06-01 00:00:00')
;
CREATE TABLE activities
([caseno] int, [activity] int, [team] varchar(1))
;
INSERT INTO activities
([caseno], [activity], [team])
VALUES
(1, 00, 'A'),
(1, 10, 'A'),
(1, 00, 'A'),
(1, 00, 'B'),
(1, 90, 'C'),
(1, 00, 'C'),
(1, 00, 'A'),
(2, 10, 'A'),
(2, 00, 'A'),
(2, 00, 'B'),
(3, 90, 'C'),
(3, 00, 'C')
;
I'm interested in aggregating the activities data, for activity = '00', split by team, and attaching to the cases data.
I've achieved this in the following way but I suspect it is not optimal. The cases table is about 1million rows and activities table is 200million rows or so.
SELECT T.*, A.A, B.B, C.C FROM cases T
LEFT JOIN (SELECT caseno, COUNT(*) AS A FROM activities WHERE activity = '00' AND team = 'A' GROUP BY caseno) A ON T.[caseno] = A.[caseno]
LEFT JOIN (SELECT caseno, COUNT(*) AS B FROM activities WHERE activity = '00' AND team = 'B' GROUP BY caseno) B ON T.[caseno] = B.[caseno]
LEFT JOIN (SELECT caseno, COUNT(*) AS C FROM activities WHERE activity = '00' AND team = 'C' GROUP BY caseno) C ON T.[caseno] = C.[caseno]
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=92632c9af821935790a7986e6f654b13
You could use conditional aggregation:
SELECT c.caseno, case_detail, date_received,
COUNT(CASE WHEN team = 'A' THEN 1 END) AS a,
COUNT(CASE WHEN team = 'B' THEN 1 END) AS b,
COUNT(CASE WHEN team = 'C' THEN 1 END) AS c
FROM cases c
LEFT JOIN activities a
ON c.caseno = a.caseno
AND a.activity = '00'
GROUP BY c.caseno, case_detail, date_received;
db<>fiddle demo
EDIT
Without typing all columns in GROUP BY:
WITH cte AS (
SELECT c.caseno,
COUNT(CASE WHEN team = 'A' THEN 1 END) AS a,
COUNT(CASE WHEN team = 'B' THEN 1 END) AS b,
COUNT(CASE WHEN team = 'C' THEN 1 END) AS c
FROM cases c
LEFT JOIN activities a
ON c.caseno = a.caseno
AND a.activity = '00'
GROUP BY c.caseno -- only PK
)
SELECT * FROM cte JOIN cases c ON cte.caseno = c.caseno;
Pivot solution
select *
from
( select cs.*,A.team
from cases cs
join activities a on cs.caseno=a.caseno and a.activity = 00
) C
pivot
(count(team)
for team in ([A],[B],[C])
) pvt
give us same result
sample

SQL count multiple cells as combination

I have the following SQL table which shows case number and their value, the case number always appear 2 cases in a group, I want to count how many the combinations with same case number appearing in the table. Be ware the order could be different, see case A and C, both of them should be count as the same combination.
case value
A 1992
A 1956
B 2000
B 2001
C 1956
C 1992
The goal is to get the total number of each combination, so the output format doesn't matter. One of expected result:
Seq value frequency
1 1992 2
1 1956 2
2 2000 1
2 2001 1
What about if there are 3 cases as a combination?
This works with any number of values for any case. It only increment frequency count when cases have the same number of values and each one have a match, no matter in which order.
CREATE TABLE #Table1
([Case] varchar(1), [Value] int)
;
INSERT INTO #Table1
([Case], [Value])
VALUES
('A', 1992), ('A', 1956), ('A', 1997), ('B', 2000), ('B', 2001), ('C', 1956),
('C', 1992), ('C', 1997), /*('C',1993),*/ ('D', 2005), ('D', 2008), ('E', 1956),
('E', 1992) , ('F', 1956), ('F', 1992), ('G', 1956), ('G', 1992) ;
--Query
select min(a.[Case]) [Case], [Values], count(*) frequency
from (
SELECT t.[Case],
stuff(
(
select ',' + cast (t1.[Value] as varchar(20))
from #table1 t1
where t1.[Case] = t.[Case]
order by t1.[Value]
for xml path('')
),1,1,'') [Values]
FROM #table1 t
GROUP BY t.[Case]
)a
group by [Values]
order by [Case]
Result whith values sorted in ascending order
Case Values frequency
A 1956,1992,1997 2
B 2000,2001 1
D 2005,2008 1
E 1956,1992 3
Data sample, SQL Server 2014
CREATE TABLE Table1
([case] varchar(1), [value] int)
;
INSERT INTO Table1
([case], [value])
VALUES
('A', 1992),
('A', 1956),
('B', 2000),
('B', 2001),
('C', 1956),
('C', 1992)
;
Query
select dense_rank() over (ORDER BY min(a.[case])) seq, a.value, count(*) freq
from table1 a left join table1 b
on a.value=b.value and a.[case]<>b.[case]
group by a.value
order by a.value
http://sqlfiddle.com/#!6/40a87/3
This is not exactly as you post expected results, but respond on what you just request in the previous comment.
select min, max, count (*) freq
from (
select a.[case] [case], min(a.value) min, max(a.value) max
from table1 a
group by a.[case]) b
group by min, max
order by min, max
http://sqlfiddle.com/#!6/40a87/18

SQL optimization for large data_sets

i got some nasty sql performance issue. I need to execute statment like:
SELECT *
FROM (SELECT /*+ FIRST_ROWS(26) */
a.*, ROWNUM rnum
FROM (SELECT *
FROM t1
WHERE t1_col1 = 'val1'
AND g_dom in ('1', '2', '3')
AND g_context IN ('3', '4', '5', '6')
AND i_col = 1
AND f_col in ('1', '2', '3', '4')
AND e_g IN (SELECT e_g
FROM t2
WHERE t2_col1 = 'val1'
AND g_context IN ('3', '4', '5', '6')
AND val like 'some val%')
ORDER BY order_id DESC) a)
WHERE rnum > 0;
Basically we got table t1 (our data table), and t2 (our support values). We got 1kk records in t1 and 10kk in t2. Column g_context narrows our data sets, but still, val had something like 500k records. We need 25 rows ordered by order_id.
Is there any way to tell inner statement
SELECT e_g FROM t2 WHERE t2_col1='val1' AND g_context IN('3','4','5','6' ) AND val like 'some val%
to get only 25 records that's match out outer statement criteria ?
Why not move rownum and the hint into the inner query like so:
SELECT t1.*,row_number() over (order by order_id desc) rn /*+ FIRST_ROWS(26) */
FROM t1
WHERE t1_col1 = 'val1'
AND g_dom in ('1', '2', '3')
AND g_context IN ('3', '4', '5', '6')
AND i_col = 1
AND f_col in ('1', '2', '3', '4')
AND e_g IN (SELECT e_g
FROM t2
WHERE t2_col1 = 'val1'
AND g_context IN ('3', '4', '5', '6')
AND val like 'some val%')
ORDER BY order_id DESC
Too me this extra subselect with hint and rownum does not seem to make any sense.
And the where-clause should be "WHERE rnum < 26", shouldn't it?

Is there any way to order the result set by what you want in SQL Server?

Is there any way to select from SQL Server by 'Queue, serie ...'.
For example I want to get some rows by using identifier.
I want to get rows ordered by like C, D, A, F
SELECT *
FROM BRANCH
WHERE IDENTIFIER IN ('C', 'D', 'A', 'F')
And this query turns rows order by random.
Maybe ordered as
'F', 'D', 'A', 'C'
'A', 'B', 'C', 'D'
How can I get the result set ordered as 'C', 'D', 'A', 'F'? I need this using for for xml path usage.
SELECT b.*
FROM dbo.BRANCH b
JOIN (
VALUES
(1, 'C'),
(2, 'D'),
(3, 'A'),
(4, 'F')
) c(ID, IDENTIFIER) ON c.IDENTIFIER = b.IDENTIFIER
ORDER BY c.ID