How to compare two rows in SQL Server - sql

I'm used to mysql when you can do that with no problems. I would like to run the following statement in SQL Server however it doesn't see the column C_COUNT.
SELECT
A.customers AS CUSTOMERS,
(SELECT COUNT(ID) FROM Partners_customers B WHERE A.ID = B.PIID) AS C_COUNT
FROM Partners A
WHERE CUSTOMERS <> [C_COUNT]
Is it possible to utilize any mathematical operations in the SELECT area like
SELECT (CUSTOMERS - C_COUNT) AS DIFFERENCE

SQL Server does not allow you to use aliases in the WHERE clause. You'll have to have something like this:
SELECT *, Customers - C_COUNT "Difference"
FROM (
SELECT
A.customers AS CUSTOMERS,
(SELECT COUNT(ID)
FROM Partners_customers B WHERE A.ID = B.PIID)
AS C_COUNT FROM Partners A
) t
WHERE CUSTOMERS <> [C_COUNT]
Or, better yet, eliminating an inline count:
select A.customers, count(b.id)
FROM Partners A
LEFT JOIN Partners_customers B ON A.ID = B.PIID
Group By A.ID
having a.customers <> count(b.id)

WITH A AS
(
SELECT
A.customers AS CUSTOMERS,
(SELECT COUNT(ID) FROM Partners_customers B WHERE A.ID = B.PIID) AS C_COUNT
FROM Partners A
WHERE CUSTOMERS <> [C_COUNT]
)
SELECT
*,
(CUSTOMERS - C_COUNT) AS DIFFERENCE
FROM A

Completely untested....
(select * from TabA
minus
select * from TabB) -- Rows in TabA not in TabB
union all
(
select * from TabB
minus
select * from TabA
) -- rows in TabB not in TabA

Related

Easy way to get a single resultset from three identical tables?

The DB I'm working with has three tables with identical column layouts, OPEX, NOPEX and CAPEX. I would like to query all three for items with a matching AssetId and get a single result set so that I can process them all at the same time in my .Net code.
The twist is that I do need to know which table they came from.
I know I can do this with a series of CASE in the SELECT clause, perhaps using the ID column in each where it's non-zero to decide which of the tables it came from. But I would have to have one for each column and the tables are pretty wide.
Is there some other way to solve this problem?
In order to get them into one set, you would use a combination of UNION and EXISTS() checks. The UNION ALL will give you a single result set that contains data from all three tables, and the EXISTS check on each will confirm the table you are querying from has corresponding records in the other tables.
SELECT *, 'OPEX' AS table_name
FROM OPEX o
WHERE EXISTS (
SELECT 1
FROM NOPEX n
WHERE n.asset_id = o.asset_id)
AND EXISTS (
SELECT 1
FROM CAPEX c
WHERE c.asset_id = o.asset_id)
UNION ALL
SELECT *, 'NOPEX' AS table_name
FROM NOPEX n
WHERE EXISTS (
SELECT 1
FROM Opex o
WHERE o.asset_id = n.asset_id)
AND EXISTS (
SELECT 1
FROM CAPEX c
WHERE c.asset_id = n.asset_id)
UNION ALL
SELECT *, 'CAPEX' AS table_name
FROM CAPEX c
WHERE EXISTS (
SELECT 1
FROM Opex o
WHERE o.asset_id = c.asset_id)
AND EXISTS (
SELECT 1
FROM NOPEX n
WHERE n.asset_id = c.asset_id)
I guess you could also do INNER JOINs?
SELECT c.*, 'CAPEX' AS table_name
FROM CAPEX c
INNER JOIN OPEX o
ON o.asset_id = c.asset_id
INNER JOIN NOPEX n
ON n.asset_id = c.asset_id
UNION ALL
SELECT o.*, 'OPEX' AS table_name
FROM OPEX o
INNER JOIN CAPEX c
ON c.asset_id = o.asset_id
INNER JOIN NOPEX n
ON n.asset_id = o.asset_id
UNION ALL
SELECT n.*, 'NOPEX' AS table_name
FROM NOPEX n
INNER JOIN OPEX o
ON o.asset_id = n.asset_id
INNER JOIN CAPEX c
ON c.asset_id = n.asset_id
Similar answer to dfundako, but resolving sooner where AssetId is in all three tables and less hitting of the indexes on the related tables:
;with cte as (
select
AssetID
from (
select distinct
AssetID
from Opex
union all
select distinct
AssetID
from Nopex
union all
select distinct
AssetID
from Capex
) as AssetIDs
group by AssetId
having count(AssetId) = 3
)
select 'Opex', * from Opex as o
inner join cte
on o.AssetID = cte.AssetID
union all
select 'Nopex', * from Nopex as n
inner join cte
on n.AssetID = cte.AssetID
union all
select 'Capex', * from Capex as c
inner join cte
on c.AssetID = cte.AssetID

Main query results in Subquery

How can we use reference of main query result set as a source table in subquery
Table A, Table C
Select
(Select * From a)
From
(Select tabA.*
From A tabA
Join C tabC
On tabA.id = tabC.id) as a
I got invalid object a error here
Presumably, you want a common table expression (CTE):
with a as (
select tabA.*
from A tabA Join
C tabC
on tabA.id = tabC.id
)
Select (Select * from a)
From a;
That said, your query makes no sense. The scalar subquery is probably going to be returning an error, either because of the number of rows or number of columns.
if you using sqlserver than modified your query based on below query.
select * from
(select A.* from TableA A inner join TableB B on A.EmployeeID = B.EmployeeID ) a

SQL Subquery to get first record

I need to execute a query something like below.
SELECT TO_CHAR(ROWNUM),
A.Name,
B.Order,
(SELECT * FROM (
SELECT ROUND(LAST_ORDER_AMOUNT,5) FROM ORDERS WHERE ID=A.id AND REQUEST_LEVEL='N' ORDER BY O_DATE DESC)
WHERE ROWNUM =1) AS AMOUNT
FROM Table1 A LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
But this gives me A.ID is invalid error in oracle. I need to get the first record from inner query as it will return multiple records.
Can someone please let me know how can i bind these tables to achieve my goal.
Thank you in advance.
You can rewrite subquery using WITH clause, not exactly sure on syntax but should be something like following.
WITH AmountQuery
AS (
SELECT ID
,ROUND(LAST_ORDER_AMOUNT, 5) AS AmountValue
,ROW_NUMBER() OVER ( ORDER BY O_DATE DESC ) AS RN
FROM ORDERS
WHERE REQUEST_LEVEL = 'N'
)
SELECT TO_CHAR(ROWNUM)
,A.Name
,B.Order
,C.AmountValue
FROM Table1 A
LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
LEFT JOIN AmountQuery C
ON a.ID = c.ID
AND c.RN = 1
here is SQLFiddle to show how it works.
http://sqlfiddle.com/#!4/696b6/36
Probably, LIMIT will do the job for you selecting just one record from the subquery (It worked for me in MySQL. I do not have Oracle, but I think it may be similar). Try something like this:
SELECT TO_CHAR(ROWNUM),
A.Name,
B.Order,
COALESCE( C.AMOUNT ) as AMOUNT,
FROM Table1 A LEFT JOIN Table2 B
ON A.TYPE_CODE = B.ENTITY_TYPE
LEFT JOIN ( SELECT ROUND(LAST_ORDER_AMOUNT,5) AS AMOUNT FROM ORDERS WHERE REQUEST_LEVEL='N' ORDER BY O_DATE DESC ) C ON C.ID = A.id
group by A.id;

LEFT JOIN - How to join tables and include extra row even if you have right match

I have two tables
Table A
-------
ID
ProductName
Table B
-------
ID
ProductID
Size
I want to join these two tables
SELECT * FROM
(SELECT * FROM A)
LEFT JOIN
(SELECT * FROM B)
ON A.ID = B.ProductID
This is easy, I will get all rows from A multiplied by rows matched in B, and NULL fields if there is no match.
But here comes the tricky question, how can I get all rows from A with NULL fields for table B, even if there is a match, so I get an extra line with NULL values plus all the matches?
SELECT A.*
, B3.ID
, B3.ProductID
, B3.Size
FROM A
LEFT JOIN
(
SELECT ProductID as MatchID
, ID
, ProductID
, Size
FROM B
UNION ALL
SELECT ID
, null
, null
, null
FROM A A2
) B3
ON A.ID = B3.MatchID
Live example at SQL Fiddle.
Instead of using UNION ALL in a subquery as suggested by others, you could also (and I would) use UNION ALL at the outer level, which keeps the query simpler:
SELECT A.ID, A.ProductName, B.ID, B.Size
FROM A
INNER JOIN B
ON B.ProductID = A.ID
UNION ALL
SELECT A.ID, A.ProductName, NULL, NULL
FROM A
Since every join is going to be successful, we can switch to a full/inner join:
SELECT
*
FROM
A
INNER JOIN
(SELECT ID,ProductID,Size FROM B
UNION ALL
SELECT NULL,ID,NULL FROM A) B
ON
A.ID = B.ProductID
Now would be a very good time to switch to naming columns explicitly, rather than using SELECT *
Or, if, as per #Andomar's comment, you need all of the B columns to be NULL:
SELECT
A.ID,A.ProductName,
B.ID,B.ProductID,B.Size
FROM
A
INNER JOIN
(SELECT ID,ProductID,Size,ProductID as MatchID FROM B
UNION ALL
SELECT NULL,NULL,NULL,ID FROM A) B
ON
A.ID = B.MatchID

SQL: Turn a subquery into a join: How to refer to outside table in nested join where clause?

I am trying to change my sub-query in to a join where it selects only one record in the sub-query. It seems to run the sub-query for each found record, taking over a minute to execute:
select afield1, afield2, (
select top 1 b.field1
from anothertable as b
where b.aForeignKey = a.id
order by field1
) as bfield1
from sometable as a
If I try to only select related records, it doesn't know how to bind a.id in the nested select.
select afield1, afield2, bfield1
from sometable a left join (
select top 1 id, bfield, aForeignKey
from anothertable
where anothertable.aForeignKey = a.id
order by bfield) b on
b.aForeignKey = a.id
-- Results in the multi-part identifier "a.id" could not be bound
If I hard code values in the nested where clause, the select duration drops from 60 seconds to under five. Anyone have any suggestions on how to join the two tables while not processing every record in the inner table?
EDIT:
I ended up adding
left outer join (
select *, row_number() over (partition by / order by) as rank) b on
b.aforeignkey = a.id and b.rank = 1
went from ~50 seconds to 8 for 22M rows.
Try this:
WITH qry AS
(
SELECT afield1,
afield2,
b.field1 AS bfield1,
ROW_NUMBER() OVER(PARTITION BY a.id ORDER BY field1) rn
FROM sometable a LEFT JOIN anothertable b
ON b.aForeignKey = a.id
)
SELECT *
FROM qry
WHERE rn = 1
Try this
select afield1,
afield2,
bfield1
from sometable a
left join
(select top 1 id, bfield, aForeignKey from anothertable where aForeignKey in(a.id) order by bfield) b on b.aForeignKey = a.id