Useage of aggregate function but without having clause - sql

Im have the following two tables created:
create table partei(
id int not null primary key ,
name varchar(20),
vorsitzender varchar(20)
);
create table abgeordneter(
name varchar(20),
partei int references partei ,
wahlkreis varchar(20)
)
How can I change this Select-Statement:
SELECT a.Partei
FROM Abgeordneter a, Partei p
WHERE a.Partei = p.ID
GROUP BY a.Partei
HAVING COUNT(a.Name) < 5
Into a statement which doesn't use the having clause, but delivers exactly the same results? Is it even possible?

You can use a subquery an eliminate the JOIN:
SELECT Partei
FROM (SELECT a.Partei, COUNT(*) as cnt
FROM Abgeordneter ap
GROUP BY a.Partei
) a
WHERE cnt < 5;

Related

SQL- How to select data from two different table?

I am working to select data from 2 different table but I can't figured out. If I use INNER JOIN it show noting. Any help are welcome and Thanks.
My First table:
CREATE TABLE P_N(
PN_ID int NOT NULL,
PN VARCHAR (1000),
primary key (PN_ID)
);
My second Table:
CREATE TABLE NAME (
NAME_ID VARCHAR(60) PRIMARY key,
NAME VARCHAR (40)
);
My select code :
SELECT DISTINCT NAME.NAME_ID, PN.PN_ID
FROM NAME
FULL JOIN P_N
ON PN.PN =NAME.NAME_ID;
If I use left or full Join this is the result:
NAME_ID PN_ID
nm0006300 NULL
nm0006400 NULL
nm0006500 NULL
nm0006600 NULL
nm0006700 NULL
AND if I use right join:
NAME_ID PN_ID
null 921691
null 921692
null 921693
null 921694
This is what I want the result to looks like For example:
NAME_ID PN_ID
nm0006300 921691
nm0006400 921692
nm0006500 921693
nm0006600 921694
You don't seem to have a JOIN key. You can add one with ROW_NUMBER():
SELECT n.NAME_ID, PN.PN_ID
FROM (SELECT n.*, ROW_NUMBER() OVER (ORDER BY NAME_ID) as seqnum
FROM NAME n
) n JOIN
(SELECT pn.*, ROW_NUMBER() OVER (ORDER BY PN) as seqnum
FROM P_N pn
) pn
ON PN.seqnum = n.seqnum;
try this
select DISTINCT NAME.NAME_ID, PN.PN_ID
from NAME,P_N as PN
where PN.PN =NAME.NAME_ID

Problem in querying specific data based on values present in different rows of the same column

I am creating a database of a college and the tables are given as:
create table depts(
deptcode char(3) primary key,
deptname char(70) not null);
create table students(
rollno number(2) primary key,
name char(50),
bdate date check(bdate < TO_DATE('2004-01-01','YYYY-MM-DD')),
deptcode char(3) references depts(deptcode)
on delete cascade,
hostel number check(hostel<20),
parent_inc number(8,1));
create table faculty(
fac_code char(2) primary key,
fac_name char(50) not null,
fac_dept char(3) references depts(deptcode)
on delete cascade);
//for courses offered by the college
create table crs_offrd(
crs_code char(5) primary key,
crs_name char(35) not null,
crs_credits number(2,1),
crs_fac_cd char(2) references faculty(fac_code)
on delete cascade);
// for course registered by students*
create table crs_regd(
crs_rollno number(2) references students(rollno),
crs_cd char(5) references crs_offrd(crs_code)
on delete cascade,
marks number(5,2),
primary key(crs_rollno,crs_cd));
I am trying to find out name , subject and marks of students who have marks more than rollno 92005102 for course CS103 and CS106.
I believe the table should look like this:
Name Subject Marks
XYZ CS103 92
XYZ CS106 95
I am confused how to check for marks in both CS103 and CS106 at the same time as the marks for two subjects are present in the same column in a different row, and query processes one row at a time.
I followed the question posted here but it works only for a column and it would list values separated by , but I need to get the corresponding subject name in which that marks was obtained.
If any other information required, please comment.
There are probably many ways to achieve this. One is this:
select
crs_rollno,
max(case when crs_cd = 'CS103' then marks end) as marks103,
max(case when crs_cd = 'CS106' then marks end) as marks106
from crs_regd cr
where crs_cd in ('CS103', 'CS106')
and marks >
(
select marks
from crs_regd cr92005102
where cr92005102.crs_rollno = 92005102 -- student 92005102
and cr92005102.crs_cd = cr.crs_cd -- same class
)
group by crs_rollno
having count(*) = 2 /* both courses better than 92005102 */ ;
You can join to the students table to get their name.
Just for the fun of it another approach:
with cs103 as (select * from crs_regd where crs_cd = 'CS103')
, cs106 as (select * from crs_regd where crs_cd = 'CS106')
select crs_rollno, cs103.marks as cs103_marks, cs106.marks as cs106_marks
from cs103 join cs106 using (crs_rollno)
where cs103.marks > (select marks from cs103 where crs_rollno = 92005102)
and cs106.marks > (select marks from cs106 where crs_rollno = 92005102);
You can use group by with having as following:
-- CTE IS USED TO FETCH THE REQUIRED DATA FROM TABLE USING JOINS
WITH CTE AS (
SELECT
S.NAME,
CO.CRS_NAME,
CO.CRS_CODE,
CR.MARKS,
S.ROLLNO
FROM
STUDENTS S
JOIN CRS_REGD CR ON ( S.ROLLNO = CR.CRS_ROLLNO )
JOIN CRS_OFFRD CO ON ( CR.CRS_CD = CO.CRS_CODE )
WHERE
CO.CRS_NAME IN (
'CS103',
'CS106'
)
)
-- ACTUAL LOGIC START FROM HERE
SELECT
S.NAME,
CO.CRS_NAME,
CR.MARKS
FROM
CTE C3
WHERE
C3.ROLLNO IN (
SELECT
C2.ROLLNO
FROM
CTE C1
JOIN CTE C2 ON ( C1.ROLLNO = 92005102
AND C1.ROLLNO <> C2.ROLLNO
AND C1.CRS_CODE = C2.CRS_CODE )
GROUP BY
C2.ROLLNO
HAVING ( MAX(CASE
WHEN C1.CRS_NAME = 'CS103'
AND C2.MARKS >= C1.MARKS THEN 1
END) = 1
AND MAX(CASE
WHEN C1.CRS_NAME = 'CS106'
AND C2.MARKS >= C1.MARKS THEN 1
END) = 1 )
);
Cheers!!

SQL query with GROUP BY and HAVING

I have a little problem in mi project, i'm trying to make a query on a single table but I'm not succeeding.
The table is this:
CREATE TABLE PARTITA(
COD_SFIDA VARCHAR (20) PRIMARY KEY,
DATA_P DATE NOT NULL,
RISULTATO CHAR (3) NOT NULL,
COD_DECK_IC VARCHAR (15),
COD_DECK_FC VARCHAR (15),
COD_EVT VARCHAR (15),
TAG_USR_IC VARCHAR (15),
TAG_USR_FC VARCHAR (15),
CONSTRAINT CHECK_RISULTATO CHECK (RISULTATO='0-0' OR RISULTATO='0-1' OR RISULTATO='1-0' OR RISULTATO='1-1'),
CONSTRAINT FK8 FOREIGN KEY (COD_DECK_IC, TAG_USR_IC) REFERENCES DECK (COD_DECK, TAG_USR) ON DELETE CASCADE,
CONSTRAINT FK17 FOREIGN KEY (COD_DECK_FC, TAG_USR_FC) REFERENCES DECK (COD_DECK, TAG_USR) ON DELETE CASCADE,
CONSTRAINT FK9 FOREIGN KEY (COD_EVT) REFERENCES TORNEO (COD_EVENTO) ON DELETE CASCADE
);
I would like to view the most used deck by each user.
this is the query I tried to do:
SELECT P.COD_DECK_FC, P.TAG_USR_FC, COUNT(P.COD_DECK_FC)
FROM PARTITA P
GROUP BY P.TAG_USR_FC, P.COD_DECK_FC
UNION
SELECT P.COD_DECK_IC, P.TAG_USR_IC, COUNT(P.COD_DECK_IC)
FROM PARTITA P
GROUP BY P.TAG_USR_IC, P.COD_DECK_IC
/
But I would like to view just the most used deck by each user and don't all the decks and how many times users used them.
How can I do?
I would like the query to show the tag_usr and the cod_deck that is used the most for all of this for each user
eg:
cod_deck tag_usr count(cod_deck)
------------- ----------- --------------
1 A1BE2 5
2 AE3NF 6
5 FNKJD 3
instead the previious query returns to me:
cod_deck tag_usr count(cod_deck)
------------- ----------- --------------
1 A1BE2 5
2 AE3NF 6
5 FNKJD 3
2 A1BE2 2
1 AE3NF 3
I just want that the query show me the users A1BE2 and AE3NF just one time, because the query have to select the most used deck of each user.
You don't want to select a field that you're counting. Try something like this:
SELECT P.COD_DECK_FC, P.TAG_USR_FC, COUNT(P.COD_SFIDA)
FROM PARTITA P
GROUP BY P.COD_DECK_FC, P.TAG_USR_FC
UNION
SELECT P.COD_DECK_IC, P.TAG_USR_IC, COUNT(P.COD_SFIDA)
FROM PARTITA P
GROUP BY P.COD_DECK_IC, P.TAG_USR_IC
That will list all of the combinations of COD_DECK_FC and TAG_USR_FC
and then number of times it appears in the table, and then do the same with COD_DECK_IC and TAG_USR_IC. It's not clear to me from your question exactly what you want, but I know that you shouldn't put a field in COUNT if you're selecting it.
If i understand correctly you need subquery with ranking function :
with t as (
select *, row_number() over (partition by cod_deck order by count desc) Seq
from (<union query>)
)
select *
from cte c
where seq = 1;
I think you want this:
with ct as (
select P.COD_DECK_FC as deck, P.TAG_USR_FC as usr, COUNT(P.COD_DECK_FC) as cnt
from partita p
group by P.TAG_USR_FC, P.COD_DECK_FC
union all
select P.COD_DECK_IC, P.TAG_USR_IC, COUNT(P.COD_DECK_IC)
from partita P
group by P.TAG_USR_IC, P.COD_DECK_IC
)
select ct.*
from (select ct.*,
row_number() over (partition by usr order by cnt desc) as seqnum
from ct
) ct
where seqnum = 1;
You can also shorten this using grouping sets:
select p.*
from (select coalesce(P.COD_DECK_FC, P.COD_DECK_IC) as deck,
coalesce(P.TAG_USR_FC, P.TAG_USR_IC) as usr,
count(*) as cnt,
row_number() over (partition by coalesce(P.TAG_USR_FC, P.TAG_USR_IC) order by count(*) desc) as seqnum
from partita p
group by grouping sets ( (P.TAG_USR_FC, P.COD_DECK_FC), P.TAG_USR_IC, P.COD_DECK_IC) )
) p
where seqnum = 1;

order by Item must appear in the select list if distinct is used

Need to return a temp table in SQL joining another temp table using DISTINCT and ORDER BY clause.
I have a declared a table which returns a few things.
Declare #GrpItems TABLE (ID INT,
Name NVARCHAR(32),
Date DATETIME,
City NVARCHAR(32),
CityCode NVARCHAR(8),
CurrencySort NVARCHAR(16)
)
INSERT INTO #GrpItems
SELECT
ID, Name, Date ,
CityCodeorCaption --this can be two type based on User input CityCode or CityCaption
FROM
RepeatItemTable
Now I have a different table where I want to insert and the procedure returns that table as the final result.
DECLARE #CurrencyTable TABLE (RowNumber INT Identity (1,1),
FK_Currency INT,
Value INT,
CityCode NVARCHAR(16),
CityCaption NVARCHAR(16)
)
INSERT INTO #Currency
SELECT DISTINCT
gb.FK_Currency, cv.Value,
c.CityCode, c.CityCaption
FROM
Balance b
JOIN
Currency c ON c.PK_Currency = b.FK_Currency
JOIN
#GrpItems gi ON c.FK_Grpitem = gi.PK_Grpitem
ORDER BY
gi.CityCodeorName
I know somewhere I need group by but I am not sure or a select clause in where filter
I think
ORDER BY
gi.CityCodeOrNAME
WHEN 'City' THEN City
ELSE CityCode ASC
END
Which does not seem to work? I need the Distinct because it might break some other logic.
Select * from #CurrencyTable
You can always use group by instead of select distinct. That will solve your problem:
SELECT gb.FK_Currency, cv.Value, c.CityCode, c.CityCaption
FROM Balance b JOIN
Currency c
ON c.PK_Currency = b.FK_Currency JOIN
#GrpItems gi
ON c.FK_Grpitem = gi.PK_Grpitem
GROUP BY gb.FK_Currency, cv.Value, c.CityCode, c.CityCaption
ORDER BY MAX(gi.CityCodeorName) ;
Note the use of the aggregation function in the ORDER BY.
ORDER BY CASE WHEN CityCodeOrNAME = 'City'
THEN City
ELSE CityCode
END
In case you need differnt orders you can also separate them
ORDER BY CASE WHEN CityCodeOrNAME = 'City' THEN City END DESC,
CASE WHEN CityCodeOrNAME <> 'City' THEN CityCode END ASC

SQL Query to Return Results If All the ID's Have Matches

I have two tables:
COUNTRY, with the two columns COUNTRY_ID and PERSON_REGION_ID.
PERSON, with many columns, in which PERSON_REGION_ID column is same as COUNTRY.PERSON_REGION_ID, and PERSON_ID is ID column of PERSON.
Query is as follows:
SELECT *
from COUNTRY
where PERSON_REGION_ID IN (
SELECT PERSON_REGION_ID
FROM PERSON
WHERE PERSON_ID IN (111, 888)))
AND COUNTRY_ID = 44;
The above query gives results if any one of the ID as matches (111 or 888).
I want the query to give results if both 111 and 888 has matches else return no results.
How this can be achieved?
I would prefer using joins here
EDIT: To answer your comment, it depends if it's procedure or just query. But you declare variables and go with that
By the way, just for the record ... this is T-SQL, not oracle's syntax
declare #CountryID int -- = 44? (if for some reason you keep CountryID as type other
-- then int, just change it to correct one)
declare #Person1 int -- = 111?
declare #Person2 int -- = 888?
select C.* from Country C
join Person P1 on C.Person_Region_ID = P1.PersionRegion_ID and P1.Country_ID = #CountryID
join Person P2 on C.Person_Region_ID = P2.PersionRegion_ID and P2.Country_ID = #CountryID
where P1.PersionID = #Person1 and P2.PersionID = #Person2
One option is to use group by with having in the subquery:
select *
from COUNTRY
where PERSON_REGION_ID IN (
select PERSON_REGION_ID
from PERSON
where PERSON_ID IN ( 111, 888))
group by PERSON_REGION_ID
having count(PERSON_ID) = 2)
and COUNTRY_ID= 44;
If there are duplicate person_id's per PERSON_REGION_ID , you'll need to use distinct with the count.
You can use EXISTS() like this:
SELECT * from COUNTRY t
WHERE EXISTS(SELECT 1 FROM PERSON s
WHERE t.PERSON_REGION_ID = s.PERSON_REGION_ID
AND PERSON_ID IN ( 111, 888)
GROUP BY PERSON_REGION_ID
HAVING COUNT(DISTINCT PERSON_ID) = 2)
AND COUNTRY_ID = 44