Grouping data without an aggregate function in SQL - sql

I was wondering if anyone could lend some insight into this problem. I'm managing a number of stock portfolios which must contain the same stocks. I need to design a query that will compare all portfolios against all portfolios and return the stocks that exist in one, but not the other.
For simplicity's sake, let's say I have a table that looks like this:
stock_symbol portfolio
AAPL A
IBM A
MCD A
NFLX A
AAPL B
IBM B
MCD B
FB B
AAPL C
IBM C
MCD C
Ideally, I want the query to return something like this:
p1 p2 stock_symbol
A B NFLX
A C NFLX
B A FB
B C FB
So comparing A to B will return NFLX, while comparing B to A will return FB.
Currently, I've got a query that works with a small number of portfolios, but I'm going to be managing >20 portfolios soon. That's hundreds of comparisons. I want to use GROUP BY, but I don't have an aggregate function.
Any ideas as to what I can do? Thanks!

This type of query doesn't need group by. It needs left join. Here is an example query that should do what you want:
select p1.portfolio, p2.portfolio, p1.stock_symbol
from table p1 left join
table p2
on p1.stock_symbol = p2.stock_symbol and
p1.portfolio <> p2.portfolio
where p2.stock_symbol is null;
EDIT:
That is such a good point that p2.portfolio will be NULL. Here is a better solution:
select p1.portfolio, p2.portfolio, p1.stock_symbol
from (select distinct portfolio from table) p1 cross join
(select distinct portfolio from table) p2 left join
table sp1
on sp1.portfolio = p1.portfolio left join
table sp2
on sp1.stock_symbol = sp2.stock_symbol
where sp2.stock_symbol is null;

Give this a try...
Setup:
select * into #tbla from (
SELECT 'AAPL' stock_symbol, 'A' portfolio union all
SELECT 'IBM ', 'A' union all
SELECT 'MCD ', 'A'union all
SELECT 'NFLX', 'A'union all
SELECT 'AAPL', 'B'union all
SELECT 'IBM ', 'B'union all
SELECT 'MCD ', 'B'union all
SELECT 'FB ', 'B'union all
SELECT 'AAPL', 'C'union all
SELECT 'IBM ', 'C'union all
SELECT 'MCD ', 'C'
)a
Query:
WITH symbols
AS (
SELECT DISTINCT stock_symbol
FROM #tbla
)
,portfolios
AS (
SELECT DISTINCT portfolio
FROM #tbla
)
,mstr
AS (
SELECT *
FROM portfolios A
CROSS JOIN symbols B
)
,interim
AS (
SELECT *
FROM mstr m
WHERE NOT EXISTS (
SELECT 1
FROM #tbla A
WHERE m.portfolio = A.portfolio
AND m.stock_symbol = A.stock_symbol
)
)
SELECT A.portfolio
,b.portfolio
,b.stock_symbol
FROM interim A
CROSS JOIN interim B
WHERE A.portfolio <> B.portfolio
AND A.stock_symbol <> B.stock_symbol
Result:
portfolio portfolio stock_symbol
A B NFLX
A C NFLX
B A FB
B C FB
C B NFLX
C A FB

Related

Horizontal To Vertical Sql Server

I'm stuck with a SQL query (SQL Server) that involves converting horizontal rows to vertical rows
Below is my Query that I am trying
SELECT P AS Amount_Rs
FROM (
Select (F1.D32-F1.D20) As Profit_For_The_Period ,F3.D2 as Current_Libilities,F5.D20 As Capital_Acount,
--M1.Name As Name,
F2.D20 AS Loan_Liabilities,F4.d1 As Opening_Diff --F2.D68 As Loan,
from Folio1 As F1
--inner Join Master1 As m1 on m1.Code like '101' or m1.Code Like '102' or m1.Code Like '106' or m1.Code Like '109' or m1.Code lIke '103'
--And m1.Code=102 And m1.Code=101)
inner Join Folio1 As F2 On (F2.MasterCode=F2.MasterCode)
inner Join Folio1 As F3 On (F3.MasterCode=F3.MasterCode)
inner Join Folio1 As F4 On (F4.MasterCode=F4.MasterCode)
inner Join Folio1 As F5 On (F5.MasterCode=F5.MasterCode)
Where F1.MasterCode=109
and F2.MasterCode =106
and F3.MasterCode=103
and F4.MasterCode=102
And F5.MasterCode=101
) p UNPIVOT
( p FOR value IN
( Profit_For_The_Period,Capital_Acount, Current_Libilities, Loan_Liabilities, Opening_Diff )
) AS unvpt
Current Output:
1 12392
2 0
3 0
4 4000
5 -200
Desired Output:
1 Capital Account 12392
2 Current Assets 0
3 Current Liabilities 0
4 Loans (Liability) 4000
5 Revenue Accounts -200
Thanks !!!
I think you are looking for a pivot. Use the CASE statement with a SUM or any aggregate function in the SELECT part and a group by in the where clause, that's how I use to put rows into columns in a query when I have to in MySQL. I don't know SQL Server but I think you can do quite the same.
your conditions below
F1.MasterCode=109
and F2.MasterCode =106
and F3.MasterCode=103
and F4.MasterCode=102
And F5.MasterCode=101
shouldn't be in the the where clause but with the case in the select part
example :
select whatever,
case when F2.MasterCode =106 then sum(column_name)
end case as column_alias, (other columns) from ...
hope this could help

Sql code for distinct fields

I was wondering if anyone can help me with this query.
I have two tables that I join together (DDS2ENVR.QBO AND KCA0001.ORTS)
THE QBO Table has a field labeled NIIN AND RIC. THE KCA0001.ORTS table has a field named SERVICE and OWN_RIC.
I Join the tables by QBO.RIC and ORTS.OWN_RIC. My dilemma is that under the NIIN field multiple rows can be identical but have different values for RIC.
Example:
NIIN RIC
123455 A
122222 B
123456 C
122222 A
I want to query a distinct count for NIINS that separates by the different service where it does not overlap. So example NIIN should only find distinct values only associated with A where the same NIIN is not found in B,C,D etc.
SELECT D.SERVICE, COUNT(C.NIIN)
FROM DDS2ENVR.QBO C
JOIN KCA0001.ORTS D ON D.OWN_RIC = C.RIC
WHERE C.SITE_ID = ('HEAA')
GROUP BY D.SERVICE
HAVING COUNT(DISTINCT C.NIIN) > 1
Please ask questions if this does not make any sense.
Using Not Exists
SELECT D.SERVICE, COUNT(C.NIIN)
FROM DDS2ENVR.QBO C
JOIN KCA0001.ORTS D ON D.OWN_RIC = C.RIC
WHERE C.SITE_ID = ('HEAA')
and NOT EXISTS (Select 1 from DDS2ENVR.QBO C1 where C1.NIIN = C.NIIN and C1.RIC <> C.RIC)
GROUP BY D.SERVICE
HAVING COUNT(DISTINCT C.NIIN) > 1
Also if the table DDS2ENVR.QBO doesn't contain duplicates and your dbms supports CTE
With cte as
(Select NIIN from DDS2ENVR.QBO group by NIIN having count(*) = 1)
SELECT D.SERVICE, COUNT(C.NIIN)
FROM DDS2ENVR.QBO C
JOIN KCA0001.ORTS D ON D.OWN_RIC = C.RIC
WHERE C.SITE_ID = ('HEAA')
and C.NIIN in (Select * from cte)
GROUP BY D.SERVICE
HAVING COUNT(DISTINCT C.NIIN) > 1

SQL queries: find common parts which price has gone up from one type to another

I have a table in the database (big orange one) including parts and prices for two different type. I am looking to find the little orange table as result in summary:
I am looking for common parts in both type R and O Where price has gone up from type O to type R.
This is the script I tried but it is disconnected:
SELECT *FROM Table WHERE type='R'as a
SELECT * FROM Table WHERE type='O'as b
SELECT * FROM a
INNER JOIN b ON a.part = b.part
WHERE a.price < b.price
Please try this and let me know,
SELECT * FROM Table a, Table b
WHERE a.type = 'R'
and b.type = 'O'
and a.part = b.part
and a.price < b.price;
I just found the answer myself:
SELECT * INTO h
FROM table AS t
WHERE t.type='R';
SELECT * INTO b
FROM table AS tab
WHERE tab.type ='O';
SELECT h.type, h.part, h.price, b.price, (b.price - h.price) AS Gap
FROM h
INNER JOIN b ON h.part = b.part;

SQL - Select records not present in another table (3 table relation)

I have 3 tables:
Table_Cars
-id_car
-description
Table_CarDocuments
-id_car
-id_documentType
-path_to_document
Table_DocumentTypes
-id_documentType
-description
I want to select all cars that do NOT have documents on the table Table_CarDocuments with 4 specific id_documentType.
Something like this:
Car1 | TaxDocument
Car1 | KeyDocument
Car2 | TaxDocument
With this i know that i'm missing 2 documents of car1 and 1 document of car2.
You are looking for missing car documents. So cross join cars and document types and look for combinations NOT IN the car douments table.
select c.description as car, dt.description as doctype
from table_cars c
cross join table_documenttypes dt
where (c.id_car, dt.id_documenttype) not in
(
select cd.id_car, cd.id_documenttype
from table_cardocuments cd
);
UPDATE: It shows that SQL Server's IN clause is very limited and not capable of dealing with value lists. But a NOT IN clause can easily be replaced by NOT EXISTS:
select c.description as car, dt.description as doctype
from table_cars c
cross join table_documenttypes dt
where not exists
(
select *
from table_cardocuments cd
where cd.id_car = c.id_car
and cd.id_documenttype = dt.id_documenttype
);
UPDATE: As you are only interested in particular id_documenttype (for which you'd have to add and dt.id_documenttype in (1, 2, 3, 4) to the query), you can generate records for them on-the-fly instead of having to read the table_documenttypes.
In order to do that replace
cross join table_documenttypes dt
with
cross join (values (1), (2), (3), (4)) as dt(id_documentType)
You can use the query below to get the result:
SELECT
c.description,
dt.description
FROM
Table_Cars c
JOIN Table_CarDocuments cd ON c.id_car = cd.id_car
JOIN Table_DocumentTypes dt ON cd.id_documentType = dt.id_documentType
WHERE
dt.id_documentType NOT IN (1, 2, 3, 4) --replace with your document type id
Thanks to #Thorsten Kettner help
select c.description as car, dt.description as doctype
from table_cars c
cross join table_documenttypes dt
where dt.id no in (
(
select cd.id_documentType
from table_cardocuments cd
where cd.idcar = c.id AND cd.id_doctype = dt.id
)
AND dt.id IN (1, 2, 3, 4)
This can be a complicated query. The idea is to generate all combinations of cars and the four documents that you want (using cross join). Then use left join to determine if the document actually exists:
select c.id_car, dd.doctype
from cars c cross join
(select 'doc1' as doctype union all
select 'doc2' union all
select 'doc3' union all
select 'doc4'
) dd left join
CarDocuments cd
on c.id_car = cd.id_car left join
Documents d
on cd.id_document_type = d.id_document_type and d.doctype = dd.doctype
where dd.id_document_type is null;
Finally, the where clause finds the car/doctype pairs that are not present in the data.

Can alias column used in a view for calculation in some other column?

CREATE VIEW dbo.myview1 As
SELECT
a.Id ,
a.Name ,
a.Age ,
CASE
WHEN b.PmtSched ='Monthly' THEN 12
WHEN b.PmtSched ='Quarterly' THEN 4
WHEN b.PmtSched ='Semi-Annual' THEN 2
WHEN b.PmtSched ='Annually' THEN 1
ELSE 12
END AS ABC,
SUM(a.Amount) *50 as TotalAmount ,
(a.AmtSpent - TotalAmount) * ABC as TOTALSPENDS
FROM dbo.User a join dbo.Details b on a.Id = b.Id
Here ABC and TotalAmount are Alias columns which needs to be used in computation in view and i am not able to use them.how to achieve this ?is there any way we could do this or we cant ?please help.
The simple solution to your problem is to repeat the expression, use a subquery, or use a CTE.
However, the more intelligent method is to add a reference table for payment schedules. This would look like:
create table PaymentSchedules (
PaymentScheduleId int identity(1, 1) primary key,
ScheduleName varchar(255),
FrequencyPerYear float -- this could be less often than once per year
);
Then the view would look like:
CREATE VIEW dbo.myview1 As
SELECT a.Id, a.Name, a.Age, ps.FrequencyPerYear,
SUM(a.Amount) * 50 as TotalAmount,
(a.AmtSpent - SUM(a.Amount) * 50) * ps.FrequencyPerYear as TOTALSPENDS
FROM dbo.User a join
dbo.Details b
on a.Id = b.Id join
dbo.PaymentSchedules ps
on ps.PaymentScheduleId = a.PamentScheduleId;
Yes, you can use it and you don't need neither subqueries, nor CTEs. It's a simple CROSS APPLY. It's quite elegant and doesn't hurt readability. If you need more information, read here.
Please see this example:
CREATE VIEW dbo.myview1
AS
SELECT A.Id
, A.Name
, A.Age
, SUM(A.Amount) * 50 AS TotalAmount
, (A.AmtSpent - TotalAmount) * T.ABC AS TotalSpends
FROM dbo.[User] AS A
CROSS APPLY (
SELECT CASE B.PmtSched
WHEN 'Monthly' THEN 12
WHEN 'Quarterly' THEN 4
WHEN 'Semi-Annual' THEN 2
WHEN 'Annually' THEN 1
ELSE 12
END) AS T(ABC)
INNER JOIN dbo.Details AS B
ON A.Id = B.Id;
Simple answer "NO"
It's impossible to do this without a subquery.
or
You need to use CTE
Below query will help you to get what you need.
;WITH Amount
(
SELECT a.Id,a.NAME,a.Age,a.AmtSpent,
CASE WHEN b.PmtSched ='Monthly' THEN 12
WHEN b.PmtSched ='Quarterly' THEN 4
WHEN b.PmtSched ='Semi-Annual' THEN 2
WHEN b.PmtSched ='Annually' THEN 1
ELSE 12
END AS ABC
,SUM(a.Amount) * 50 AS TotalAmount
FROM
dbo.[User] a
INNER JOIN
dbo.Details b ON a.Id = b.Id
GROUP BY id, NAME, age, abc, a.AmtSpent, TotalAmount
)
Now you can call those alias for calculation.
SELECT id,NAME,age,abc,(a.AmtSpent - TotalAmount) * ABC AS TOTALSPENDS FROM Amount