I’m new to SQL and I’m using MS Access 2016. I created a query using Table1 and Table2.
Table1 holds the data A, B, C, D.
Table2 holds values (the letters are T2Data1; and numbers are T2Data2): A 2, A 6, B 3, & D 4.
The query is:
SELECT Table1.T1Data1, Sum(Table2.T2Data2) AS SumOfT2Data2
FROM Table1 INNER JOIN Table2 ON Table1.T1Data1 = Table2.T2Data1
GROUP BY Table1.T1Data1;
Ideally I want the output to be:
A 8
B 3
C
D 4
I can get A, B, & D but I can't get the blank for C. How do I do that?? Any help is much appreciated!!
Use an outer join:
SELECT Table1.T1Data1, Sum(Table2.T2Data2) AS SumOfT2Data2
FROM Table1
LEFT JOIN Table2 ON Table1.T1Data1 = Table2.T2Data1
GROUP BY Table1.T1Data1
A Visual Explanation of SQL Joins
SQL Fiddle Demo
If you want to return 0 instead of null, you may need to use nz() which is similar to coalesce.
Related
I have done this long ago in other DBMSs (Oracle or MySQL... don't really remember) and I'm looking for the way to do this in SQL Server, if possible at all.
Suppose you have a table with several columns, say A, B, C, ... M. I wish to phrase a select from this table where columns A, B, and C display specific sets of value or, in other words, a list of values combinations.
For instance, I wish to retrieve all the records that match any of the following combinations:
A B C
1 'Apples' '2016-04-12'
56 'Cars' '2014-02-11'
....
Since the list of possible combinations may be quite long (including the option of an inner SELECT), it would not be practical to use something like:
WHERE ( A = 1 AND B = 'Apples' and C = '2016-04-12' ) OR
( A = 56 AND B = 'Cars' and C = '2014-02-11' ) OR
...
As stated, I did use this type of construct in the past and it was something like:
SELECT *
FROM MyTable
WHERE (A,B,C) IN (SELECT A,B,C FROM MYOtherTable) ;
[Most likely this syntax is wrong but it shows what I'm looking for]
Also, I would rather avoid Dynamic SQL usage.
So, the questions would be:
Is this doable in SQL Server?
If the answer is YES, how should the SELECT be phrased?
Thanks in advance.
You can use JOIN
SELECT m1.*
FROM MyTable m1
JOIN MYOtherTable m2
ON m1.A = m2.A
AND m1.B = m2.B
AND m1.C = m2.C
or Exists
SELECT m1.*
FROM MyTable m1
WHERE EXISTS (SELECT 1
FROM MYOtherTable m2
WHERE m1.A = m2.A
AND m1.B = m2.B
AND m1.C = m2.C)
I have a stored procedure in Sql Server 2008. The logic of the SP is looking like:
select Name, Surname, sum(Age)
From (
(Select t1.Name,t1.Surname,t1.sum(Age) as Age
From Table1 t1
group by t1.Name, t1.Surname) mt1
full outer join (Select t2.Name as N,t2.Surname as S,t2.sum(Age) as A
From Table2 t2
group by t2.Name, t2.Surname) mt2
on mt1.Name = mt2.N and mt1.Surname = mt2.S
) t
where sum(Age) <= 100
So this is returning something like:
Name Surname Age
A B 12
X B 13
C D 15
I have an SSRS matrix like:
Description Age
<<Expr>> sum([Age])
where Expr is: IIF(Parameters!check.Value=1,Fields!Name.Value,Fields!Surname.Value)
Without making any groupby under the related row group in SSRS2008, i am able to see it like tkat. However, when I add a groupby filter depending on "Surname", it somehow removes any Surname that occurring with at least 2 different name. So, I see only D 15 row instead of:
B 25
D 15
What is my mistake here? How can I show B 25 at this point as well? (I am supposed to not to change SP if possible)
Any help would be appreciated.
If I have Table1 as
A B C
1 b.1 c.1
2 b.2 c.2
1 b.3 c.3
My second table Table2 as
A D E F G
1 d.1 e.1 f.1 g.1
2 d.2 e.2 f.2 g.2
I need to insert into an empty Table3 the values from above such that it looks like this.
A B C D E
1 b.1 c.1 d.1 e.1
2 b.2 c.2 d.2 e.2
1 b.3 c.3 d.1 e.1
So basically I need to insert each row of Table1 into Table3. For each row I need to check for column A and find the corresponding value D and E from the column and insert into Table3. Is it possible to do this in one single query?
To copy Table1 to Table3 I can use the query
INSERT INTO Table3(A,B,C) SELECT A,B,C FROM Table1
And then I need to take each row from Table3 and using A update the values of D and E from Table2. Is there a better solution that I can use to insert directly from both tables to Table3? Any help is appreciated, as I am a beginner with database and queries.
To merge two tables, use a join:
-- INSERT ...
SELECT A, B, C, D, E
FROM Table1
JOIN Table2 USING (A);
This will not generate a result row if no matching Table2 row is found. If you want a result row in this case (with NULLs for the missing values), use an outer join instead.
INSERT INTO Table3 (A,B,C,D,E)
SELECT t1.A, t1.B, t1.C, t2.D, t2.E FROM Table1 t1
INNER JOIN Table2 t2 ON t2.A = t1.A
This might solve your problem.
I'm having trouble figuring this out.
According to Jeff Atwood A Visual Explanation of SQL Joins Left outer join produces a complete set of records from Table A, with the matching records (where available) in Table B. If there is no match, the right side will contain null.
The left table (TableA) doesn't have duplicates. The right tableB has 1 or 2 entries for each client number. The PrimaryTP designates one as primary with 1 and the other has 0.
I shouldn't have to include the line And B.PrimaryTP = 1 because TableA doesn't have duplicates. Yet if I leave it out I get duplicate client numbers. Why?
Can you help me understand how this works. It's being very confusing to me. The logic of And B.PrimaryTP = 1 escapes me. Yet it seems to work. Still, I'm scared to trust it if I don't understand it. Can you help me understand it. Or do I have a logic error hidden in the query?
SELECT A.ClientNum --returns a list with no duplicate client numbers
FROM (...<TableA>
) as A
Left Outer Join
<TableB> as B
on A.ClientNum = B.ClientNum
--eliminate mismatch of (ClientNum <> FolderNum)
Where A.ClientNum Not In
(
Select ClientNum From <TableB>
Where ClientNum Is Not Null
And ClientNum <> IsNull(FolderNum, '')
)
--eliminate case where B.PrimaryTP <> 1
And B.PrimaryTP = 1
The difference between an INNER JOIN and a LEFT JOIN is just that the LEFT JOIN still returns the rows in Table A when there are no corresponding rows in Table B.
But it's still a JOIN, which means that if there is more than one corresponding row in Table B, it will join the row from Table A to each one of them.
So if you want to make sure that you get no more than one result for each row in Table A, you have to make sure that no more than one row from Table B is found - hence the And B.PrimaryTP = 1.
If you have one client number in A and two matches in Table B, then you will get duplicates.
Suppose you have the following data,
Table-A(client Num) Table-B(client Num)
1 2
2 2
The left Join Results
Table-A(client Num) Table-B(client Num)
1 (null)
2 2
2 2
This is the cause of duplicates. So you need to take distinct values form Table B or perform Distinct on the result set.
I shouldn't have to include the line And B.PrimaryTP = 1 because TableA doesn't have duplicates. Yet if I leave it out I get duplicate client numbers. Why?
Because both rows in the right table match a row in the left table. There is no way for SQL Server to output a triangular result; it must show the columns from both tables for every joined row. And this is true for INNER JOIN as well.
DECLARE #a TABLE(a INT);
DECLARE #b TABLE(b INT);
INSERT #a VALUES(1),(2);
INSERT #b VALUES(1),(1);
SELECT a.a, b.b FROM #a AS a
LEFT OUTER JOIN #b AS b ON a.a = b.b;
SELECT a.a, b.b FROM #a AS a
INNER JOIN #b AS b ON a.a = b.b;
Results:
a b
-- ----
1 1
1 1
2 NULL
a b
-- --
1 1
1 1
On the link that you gave the joins are explained very good. So the problem is that you have several records from table A (no matter that there are no duplicates) is that to 1 record in A there are 2 records in B (in some cases). To avoid this you can use either DISTINCT clause, either GROUP BY clause.
The LEFT OUTER JOIN will give you all the records from A with all the matching records from B. The difference with an INNER JOIN is that if there are no matching records in B, an INNER join will omit the record from A entirely, while the LEFT join will then still include a row with the results from A.
In your case, however, you may also want to check out the DISTINCT keyword.
I'm trying to select records with a statement
SELECT *
FROM A
WHERE
LEFT(B, 5) IN
(SELECT * FROM
(SELECT LEFT(A.B,5), COUNT(DISTINCT A.C) c_count
FROM A
GROUP BY LEFT(B,5)
) p1
WHERE p1.c_count = 1
)
AND C IN
(SELECT * FROM
(SELECT A.C , COUNT(DISTINCT LEFT(A.B,5)) b_count
FROM A
GROUP BY C
) p2
WHERE p2.b_count = 1)
which takes a long time to run ~15 sec.
Is there a better way of writing this SQL?
If you would like to represent Set Difference (A-B) in SQL, here is solution for you.
Let's say you have two tables A and B, and you want to retrieve all records that exist only in A but not in B, where A and B have a relationship via an attribute named ID.
An efficient query for this is:
# (A-B)
SELECT DISTINCT A.* FROM (A LEFT OUTER JOIN B on A.ID=B.ID) WHERE B.ID IS NULL
-from Jayaram Timsina's blog.
You don't need to return data from the nested subqueries. I'm not sure this will make a difference withiut indexing but it's easier to read.
And EXISTS/JOIN is probably nicer IMHO then using IN
SELECT *
FROM
A
JOIN
(SELECT LEFT(B,5) AS b1
FROM A
GROUP BY LEFT(B,5)
HAVING COUNT(DISTINCT C) = 1
) t1 On LEFT(A.B, 5) = t1.b1
JOIN
(SELECT C AS C1
FROM A
GROUP BY C
HAVING COUNT(DISTINCT LEFT(B,5)) = 1
) t2 ON A.C = t2.c1
But you'll need a computed column as marc_s said at least
And 2 indexes: one on (computed, C) and another on (C, computed)
Well, not sure what you're really trying to do here - but obviously, that LEFT(B, 5) expression keeps popping up. Since you're using a function, you're giving up any chance to use an index.
What you could do in your SQL Server table is to create a computed, persisted column for that expression, and then put an index on that:
ALTER TABLE A
ADD LeftB5 AS LEFT(B, 5) PERSISTED
CREATE NONCLUSTERED INDEX IX_LeftB5 ON dbo.A(LeftB5)
Now use the new computed column LeftB5 instead of LEFT(B, 5) anywhere in your query - that should help to speed up certain lookups and GROUP BY operations.
Also - you have a GROUP BY C in there - is that column C indexed?
If you are looking for just set difference between table1 and table2,
the below query is simple that gives the rows that are in table1, but not in table2, such that both tables are instances of the same schema with column names as
columnone, columntwo, ...
with
col1 as (
select columnone from table2
),
col2 as (
select columntwo from table2
)
...
select * from table1
where (
columnone not in col1
and columntwo not in col2
...
);