Sql Query to find sum from different tables - sql

Hi i require a help in writing a query.
Tables are:
tblStandard1students
tblStandard2students
tblStandard1students
tblDivision
tblCandidateinfo
tblStandard1students,tblStandard2students,tblStandard1studentstbl contain information about students enrolled in standard 1,2 and 3
tblStandars1students
Candid admitted
1 Y
2 N
3 Y
tblDivision contains only 2 columns
ID Division
1 A
2 B
3 C
tblCandidateinfo
Candid gender Division
1 M 1
2 F 2
and so on...
Now I want the table like this
Division Students(Standard1) Students(Standard2) Students(Standard3)
M F M F M F
------------------------------------------------------------------------
A 1 0 0 0 0 1
B 2 2 3 3 4 4
C 1 0 0 0 0 0
I tried this following query:
SELECT Division,
( SELECT count(*)
FROM tblStandard1students A
INNER JOIN tblCandidateinfo B ON A.Candid=B.Candid
INNER JOIN tblDivision C ON C.ID=B.Division) AS Students(Standard1),
( SELECT count(*)
FROM tblStandard2students A
INNER JOIN tblCandidateinfo B ON A.Candid=B.Candid
INNER JOIN tblDivision C ON C.ID=B.Division) AS Students(Standard2),
( SELECT count(*)
FROM tblStandard3students A
INNER JOIN tblCandidateinfo B ON A.Candid=B.Candid
INNER JOIN tblDivision C ON C.ID=B.Division ) AS Students(Standard3)
FROM tblDivision Z
but this is only half the query i din segregate it gender wise...help me to complete it.

;WITH combined AS
(
SELECT ci.Division, 'Students(Standard1) ' + ci.gender AS grp
FROM tblCandidateInfo ci
INNER JOIN tblStandard1students s ON ci.Candid = s.Candid
UNION ALL
SELECT ci.Division, 'Students(Standard2) ' + ci.gender AS grp
FROM tblCandidateInfo ci
INNER JOIN tblStandard2students s ON ci.Candid = s.Candid
UNION ALL
SELECT ci.Division, 'Students(Standard3) ' + ci.gender AS grp
FROM tblCandidateInfo ci
INNER JOIN tblStandard1studentstbl s ON ci.Candid = s.Candid
)
SELECT Division,
[Students(Standard1) M], [Students(Standard1) F],
[Students(Standard2) M], [Students(Standard2) F],
[Students(Standard3) M], [Students(Standard3) F]
FROM
(
SELECT d.Division, grp
FROM tblDivision d
LEFT OUTER JOIN combined c ON d.ID = c.Division
) x
PIVOT
(
COUNT(grp)
FOR grp IN ([Students(Standard1) M], [Students(Standard1) F],
[Students(Standard2) M], [Students(Standard2) F],
[Students(Standard3) M], [Students(Standard3) F])
) y
ORDER BY Division

SELECT divison.Division ,IFNULL(stander1.M,0),IFNULL(stander1.F,0) FROM test.tblDivision divison
Left join (SELECT division ,count( case gender when 'M' then 1 else null end) as M,count( case gender when 'F' then 1 else null end) as F
FROM
test.tblCandidateinfo tc inner join test.tblStandars1students ts1
ON tc.Candid=ts1.Candid
group by division) as stander1 on stander1.division= divison.id
group by divison.id
;
Insted of IFNULL use ISNULL and
take left join for all standar tables

Related

How to change value if multiple duplicate rows more then 3 in Oracle SQL

I am trying to select if A.POLICY_NO or A.POLICY_TITLE values duplicate rows more than 3, want to change C.SMALL_CATEGORY_TITLE values to "Z" and also want to cahge B.SMALL_CATEGORY_SID values to null.
I used 3 tables that YIP.YOUTH_POLICY as A, YIP.YOUTH_POLICY_AREA as B and YIP.YOUTH_SMALL_CATEGORY as C. the PK and FK is POLICY_NO.
ForExample if the table
A.POLICY_NO
B.SMALL_CATEGORY_SID
C.SMALL_CATEGORY_TITLE
A.POLICY_TITLE
1
80
A
VALUE1
1
90
B
VALUE1
1
95
C
VALUE1
2
80
A
VALUE2
2
90
B
VALUE2
2
95
C
VALUE2
3
80
A
VALUE3
3
90
B
VALUE3
4
80
A
VALUE4
I wnat to select like
A.POLICY_NO
B.SMALL_CATEGORY_SID
C.SMALL_CATEGORY_TITLE
A.POLICY_TITLE
1
NULL
Z
VALUE1
2
NULL
Z
VALUE2
3
80
A
VALUE3
3
90
B
VALUE3
4
80
A
VALUE4
this is the query select except when duplicate values more then 3,
SELECT
A.POLICY_NO
, B.SMALL_CATEGORY_SID
, C.SMALL_CATEGORY_TITLE
, A.POLICY_TITLE
, COUNT(*) OVER() AS TOTAL_COUNT
FROM
YIP.YOUTH_POLICY A
LEFT JOIN
YIP.YOUTH_POLICY_AREA B
ON A.POLICY_NO = B.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY C
ON B.SMALL_CATEGORY_SID = C.SMALL_CATEGORY_SID
WHERE A.POLICY_NO IN (SELECT
F.POLICY_NO
FROM YIP.YOUTH_POLICY F
LEFT JOIN
YIP.YOUTH_POLICY_AREA G
ON F.POLICY_NO = G.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY H
ON G.SMALL_CATEGORY_SID = H.SMALL_CATEGORY_SID
GROUP BY F.POLICY_NO
HAVING COUNT(*) < 3)
ORDER BY A.POLICY_NO;
and I was tried to change C.SMALL_CATEGORY_TITLE values when POLICY_NO values duplicated more then 3
SELECT
A.POLICY_NO
--, B.SMALL_CATEGORY_SID
, SUM(CASE WHEN C.SMALL_CATEGORY_TITLE IN (SELECT
F.POLICY_NO
FROM YIP.YOUTH_POLICY F
LEFT JOIN
YIP.YOUTH_POLICY_AREA G
ON F.POLICY_NO = G.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY H
ON G.SMALL_CATEGORY_SID = H.SMALL_CATEGORY_SID
GROUP BY F.POLICY_NO
HAVING COUNT(*) > 2) THEN 1 ELSE NULL END) AS 'Z'
, A.POLICY_TITLE
, COUNT(*) OVER() AS TOTAL_COUNT
FROM
YIP.YOUTH_POLICY A
LEFT JOIN
YIP.YOUTH_POLICY_AREA B
ON A.POLICY_NO = B.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY C
ON B.SMALL_CATEGORY_SID = C.SMALL_CATEGORY_SID
ORDER BY A.POLICY_NO;
I got SQL Error [42000]: JDBC-8006:Missing FROM keyword. ¶at line 17, column 59 of null:¶ HAVING COUNT(*) > 2) THEN 1 ELSE NULL END) AS 'Z'¶
Is there any way to fix it? I was thinking more then 5 hours but i could't fix it
The trick is to use the windowing function COUNT(*) OVER to get a count over the entire rowset in order to later make decisions about each individual row. You can finally collapse it down with DISTINCT or GROUP BY.
SELECT DISTINCT
policy_no,
CASE WHEN (policy_count >= 3 OR policy_title_count>= 3) THEN NULL
ELSE small_category_sid
END AS small_category_sid,
CASE WHEN (policy_count >= 3 OR policy_title_count>= 3) THEN 'Z'
ELSE small_category_title
END AS small_category_title,
policy_title
FROM (SELECT x.*,
COUNT(*) OVER (PARTITION BY policy_no) policy_count,
COUNT(*) OVER (PARTITION BY policy_title) policy_title_count
FROM yip.youth_policy x)

SQL get record when finding in 2 records

Hi I tried to build sql query to find id when is in 2 records (can be more). Let me explained by example
I have 2 tables
C
id
type_id
1
499
1
599
D
type_id
type_name
499
AN
599
DE
And I want to get id which has AN and DE
SELECT *
FROM C
INNER JOIN D
ON D.type_id = C.type_id
WHERE
EXISTS (SELECT 1 FROM D D1 WHERE D1.type_id = C.type_id AND D1.type_name = 'AN') AND
EXISTS (SELECT 1 FROM D D2 WHERE D2.type_id = C.type_id AND D2.type_name = 'DE');
But did not work .Than you for help
If you want all the data from the join then you can use analytic functions:
SELECT id,
type_id,
type_name
FROM (
SELECT c.id,
c.type_id,
d.type_name,
COUNT(CASE d.type_name WHEN 'AN' THEN 1 END) OVER (PARTITION BY c.id)
AS num_an,
COUNT(CASE d.type_name WHEN 'DE' THEN 1 END) OVER (PARTITION BY c.id)
AS num_de
FROM C
INNER JOIN D
ON D.type_id = C.type_id
WHERE d.type_name IN ('AN', 'DE')
)
WHERE num_an > 0
AND num_de > 0;
Which outputs:
ID
TYPE_ID
TYPE_NAME
1
599
DE
1
499
AN
If you just want the id then you can aggregate and use a HAVING clause:
SELECT c.id
FROM C
INNER JOIN D
ON D.type_id = C.type_id
WHERE d.type_name IN ('AN', 'DE')
GROUP BY c.id
HAVING COUNT(CASE d.type_name WHEN 'AN' THEN 1 END) > 0
AND COUNT(CASE d.type_name WHEN 'DE' THEN 1 END) > 0
Which outputs:
ID
1
fiddle
Get the distinct counts of type_name for each ID ensure count = two and limit type_name to 'AN' or 'DE'
SELECT C.ID
FROM C
INNER JOIN D
on C.type_id=D.type_id -- standard join on Type_ID
WHERE D.Type_name in ('AN','DE') -- limit records to only AN/DE since we need both.
GROUP BY C.ID -- group so we get just 1 ID
HAVING Count(Distinct Type_name) = 2 -- ensure distinct count is 2 for each C.ID.
We join the two tables
We limit to ID having either an 'AN' or DE type name
We group by ID's
We count the distinct types for each ID and if it's 2, we know we have an AN and DE type for that ID.
Count distinct is used since I'm unsure if a type_name could be duplicated for a C.ID. It looks possible given table structure. but unsure without known Pk/FK relations. distinct "Might" be able to be removed if we KNOW it's not possible.

How to get the cumulative sum of children up its parents?

So I have written a query to get the cumulative sum of children but I think partition sum has error as its totalling for the parent that is not part of the children.
My fiddle is http://sqlfiddle.com/#!15/88828/1
I have dont a running total but thats wrong. I want the siblings total to a child and child total back to its parent.So basically cumulative total of a child up the tree.
expected output
parent_child_tree id name dimensionvalueid level order_sequence volume cummulative_total
A1 1 A1 (null) 0 1 20 840
-----A1:1 2 A1:1 1 1 1_1 (null) 820
----------A1:1:1 3 A1:1:1 2 2 1_1_2 20 820
-----------A1:1:1:1 4 A1:1:1:1 3 3 1_1_2_3 300 800
-----------A1:1:1:2 5 A1:1:1:2. 3 3 1_1_2_3 500 500
B1 6 B1 (null) 0 6 200 300
-----B1:2 8 B1:2 6 1 6_6 (null) null
-----B1:1 7 B1:1 6 1 6_6 (null) 100
----------B1:2:1 9 B1:2:1 8 2 6_6_8 100 100
To get totals for tree nodes you need to generate hierarchy tree for every node in a subquery like this
SELECT
d.*,
v.volume,
(
WITH RECURSIVE cte AS (
SELECT
dd.id AS branch_id,
dd.id
FROM dimensionvalue dd
WHERE dd.id = d.id
UNION ALL
SELECT
cte.branch_id,
dd.id
FROM dimensionvalue dd
JOIN cte ON dd.dimensionvalueid = cte.id
)
SELECT SUM(v.volume)
FROM cte
JOIN valuation v ON v.dimensionvalueid = cte.id
GROUP BY cte.branch_id
) AS totals
FROM dimensionvalue d
LEFT JOIN valuation v ON v.dimensionvalueid = d.id
ORDER BY d.name;
If you really need all those "decoration" columns that you generate in your query for each tree node than you can combine your recursive CTE hierarchy with subquery for totals calculation like this
WITH RECURSIVE hierarchy AS (
SELECT
d.id,
d.name,
d.dimensionvalueid,
0 AS level,
CAST(d.id AS varchar(50)) AS order_sequence
FROM dimensionvalue d
WHERE d.dimensionvalueid IS NULL
UNION ALL
SELECT
e.id,
e.name,
e.dimensionvalueid,
hierarchy.level + 1 AS level,
CAST(hierarchy.order_sequence || '_' || CAST(hierarchy.id AS VARCHAR(50)) AS VARCHAR(50)) AS order_sequence
FROM hierarchy
JOIN dimensionvalue e ON e.dimensionvalueid = hierarchy.id
)
SELECT
RIGHT('-----------', h.level * 5) || h.name || ' ' AS parent_child_tree,
h.*,
v.volume,
(
WITH RECURSIVE cte AS (
SELECT
dd.id AS branch_id,
dd.id
FROM dimensionvalue dd
WHERE dd.id = h.id
UNION ALL
SELECT
cte.branch_id,
dd.id
FROM dimensionvalue dd
JOIN cte ON dd.dimensionvalueid = cte.id
)
SELECT SUM(v.volume)
FROM cte
JOIN valuation v ON v.dimensionvalueid = cte.id
GROUP BY cte.branch_id
) AS totals
FROM hierarchy h
LEFT JOIN valuation v ON v.dimensionvalueid = h.id
ORDER BY h.name
You can check a working demo here

Is there a way to join two queries in SQL each with an order by?

I have two queries that return data from two tables:
SELECT TOP 3
AE.id, AE.name, COUNT(R.id) 'number of reserves'
FROM
AIRPORT AE
INNER JOIN
FLY V ON V.id_destiny = AE.id
INNER JOIN
RESERVE R ON R.id_fly = V.id
GROUP BY
AE.id, AE.name
ORDER BY
COUNT(R.id) DESC;
Example of returned data:
id
name
number of reserves
6
name1
27
4
name2
18
14
name3
14
and
SELECT TOP 3
AE.id, AE.name, COUNT(R.id) 'number of reserves'
FROM
AEROPUERTO AE
LEFT JOIN
FLY V ON V.id_destiny = AE.id
LEFT JOIN
RESERVE R ON R.id_fly = V.id
GROUP BY
AE.id, AE.name
ORDER BY
COUNT(R.id) ASC;
Example of data returned from this second query:
id
name
number of reserves
7
name4
0
11
name5
0
12
name6
0
I need to combine these into a single output with the first query first (in the same order) and the second query next with the same order like this:
id
name
number of reserves
6
name1
27
4
name2
18
14
name3
14
7
name4
0
11
name5
0
12
name6
0
Is there a way to do it?
Edit: I have already tried the union all option, but I can't use the group by in each query so the table that is returned is different from what I need
(SELECT TOP 3 AE.id, AE.name, COUNT(R.id) 'number of reserves'
FROM AIRPORT AE
INNER JOIN FLY V
ON V.id_destiny = AE.id
INNER JOIN RESERVE R
ON R.id_fly = V.id
GROUP BY AE.id, AE.name)
UNION ALL
(SELECT TOP 3 AE.id, AE.name, COUNT(R.id) 'number of reserves'
FROM AEROPUERTO AE
LEFT JOIN FLY V
ON V.id_destiny= AE.id
LEFT JOIN RESERVE R
ON R.id_fly = V.id
GROUP BY AE.id, AE.name)
ORDER BY COUNT(R.id) ASC;
When you enclosed each query in parenthesis, it is acting like a derived table. You will need a SELECT clause to select from the derived table.
SELECT *
FROM
(
-- Your first query here
) AS Q1
UNION ALL
SELECT *
FROM
(
-- Your second query here
) AS Q2
You may also use CTE to do it
WITH
Q1 AS
(
-- Your first query here
),
Q2 AS
(
-- Your second query here
)
SELECT *
FROM Q1
UNION ALL
SELECT *
FROm Q2
EDIT : if you also wanted the final result in the same order in both query, add another column for final query ORDER BY
WITH
Q1 AS
(
SELECT TOP 3
AE.id, AE.name, COUNT(R.id) 'number of reserves',
Q = 1,
RN = ROW_NUMBER() OVER (ORDER BY COUNT(R.id) DESC)
FROM
AIRPORT AE
INNER JOIN
FLY V ON V.id_destiny = AE.id
INNER JOIN
RESERVE R ON R.id_fly = V.id
GROUP BY
AE.id, AE.name
ORDER BY
COUNT(R.id) DESC
),
Q2 AS
(
SELECT TOP 3
AE.id, AE.name, COUNT(R.id) 'number of reserves',
Q = 2,
RN = ROW_NUMBER() OVER (ORDER BY COUNT(R.id) DESC)
FROM
AEROPUERTO AE
LEFT JOIN
FLY V ON V.id_destiny = AE.id
LEFT JOIN
RESERVE R ON R.id_fly = V.id
GROUP BY
AE.id, AE.name
ORDER BY
COUNT(R.id) ASC
)
SELECT *
FROM Q1
UNION ALL
SELECT *
FROM Q2
ORDER BY Q, RN
I break the query and made two temp tables. You can do this in CTE (Commn Table expression) as well.
SELECT TOP 3 AE.id, AE.name, COUNT(R.id) 'number of reserves'
INTO #Temp_1
FROM AIRPORT AE
INNER JOIN FLY V
ON V.id_destiny = AE.id
INNER JOIN RESERVE R
ON R.id_fly = V.id
GROUP BY AE.id, AE.name
SELECT TOP 3 AE.id, AE.name, COUNT(R.id) 'number of reserves'
into #Temp_2
FROM AEROPUERTO AE
LEFT JOIN FLY V
ON V.id_destiny= AE.id
LEFT JOIN RESERVE R
ON R.id_fly = V.id
GROUP BY AE.id, AE.nam
SELECT *
FROM #Temp_1
UNION ALL
SELECT *
FROM #Temp_2
drop table #Temp_1
drop table #Temp_2

Left join oracle db

I have two tables and I wanna join them (outer).
day description
-----
1 mo
2 tu
...
5 fr
order day code
----
1 1 0
2 2 0
3 1 2
4 3 0
5 4 0
I wanna this table as a result:
description order code
------
mo 1 0
th 2 0
we 4 0
tu 5 0
fr null null
When I use a left join like this, the code does not work right and does not show me friday:
select *
from days d left join order o on d.id= o.day
where o.code = 0
But when I write the code like this, the code works right:
select *
from
(select * from day) d
left join
(select * from order when code = 0) o
on d.id = o.day
Does anyone know why?
Just put the condition on the left joined table in the on clause of the left join rather than in the where clause:
select d.description, o.order, o.code
from days d
left join order o on d.id= o.day and o.code = 0
Conditions in the where clause are mandatory - so the where clause eliminates rows where the left join came back empty, since o.code is null, which is not equal to 0.
on condition is used to join the table.
where condition is to filter the records from the join result.
Below will join the table on the condition of d.id = o.day
and then select records only with o.code=0
select *
from days d left join order o on d.id= o.day
where o.code = 0
In order to join on both d.id = o.day and o.code=0
you need below
select *
from days d left join order o on d.id= o.day
and o.code = 0