Count Distinct and extract Values not in Group By - sql

I am trying to get a full table with an additional column with a count distinct grouped by only a set of fields, not the full table.
Basically, I have this table:
| mandt | ktopl | yhkonto | yhhykto | yhbwkz |
|-------|-------|------------|---------|--------|
| 111 | SAG | 0034600000 | 346 | 1 |
| 111 | SAG | 0034600000 | 346 | 21 |
| 111 | SAG | 0034600000 | 346 | 82 |
| 111 | SAG | 0034600000 | 346 | 87 |
| 111 | SAG | 0039410000 | 39410 | 1 |
| 111 | SAG | 0039410000 | 39410 | 21 |
| 111 | SAG | 0039410000 | 39410 | 82 |
| 111 | SAG | 0039410000 | 39410 | 87 |
| 111 | SAG | 0039630000 | 39630 | 1 |
| 111 | SAG | 0039630000 | 39630 | 21 |
and I want to get this result, with an additional column, where I get the count of distinct values of yhhykto grouping by mandt, ktopl and yhkonto:
| mandt | ktopl | yhkonto | yhhykto | yhbwkz | cnt_yhhykto |
|-------|-------|------------|---------|--------|-------------|
| 111 | SAG | 0034600000 | 346 | 1 | 1 |
| 111 | SAG | 0034600000 | 346 | 21 | 1 |
| 111 | SAG | 0034600000 | 346 | 82 | 1 |
| 111 | SAG | 0034600000 | 346 | 87 | 1 |
| 111 | SAG | 0039410000 | 39410 | 1 | 1 |
| 111 | SAG | 0039410000 | 39410 | 21 | 1 |
| 111 | SAG | 0039410000 | 39410 | 82 | 1 |
| 111 | SAG | 0039410000 | 39410 | 87 | 1 |
| 111 | SAG | 0039630000 | 39630 | 1 | 1 |
| 111 | SAG | 0039630000 | 39630 | 21 | 1 |
I have a working query:
select distinct yh010.mandt,
yh010.ktopl,
yh010.yhkonto,
yh010.yhhykto,
yh010.yhbwkz,
yh010_x.cnt_yhhykto
from yh010
inner join (
select distinct yh010.mandt,
yh010.ktopl,
yh010.yhkonto,
count(distinct yh010.yhhykto) as cnt_yhhykto
from yh010
group by yh010.mandt, yh010.ktopl, yh010.yhkonto
) yh010_x
on yh010_x.mandt = yh010.mandt
and yh010_x.ktopl = yh010.ktopl
and yh010_x.yhkonto = yh010.yhkonto
;
But this inner join with itself doesn't seem the best solution. Or is it?
I was thinking more like in terms of:
select yh010.mandt,
yh010.ktopl,
yh010.yhkonto,
yh010.yhhykto,
yh010.yhbwkz
count( distinct yh010.yhhykto
) over ( group by yh010.mandt, yh010.ktopl, yh010.yhkonto
) as cnt_dist
from yh010
;
But Incorrect syntax near the keyword 'distinct'.
Should I keep that working query or is there any better option?
The fiddle can be found here
Thank you!

Based on this description:
I want to get this result, getting the count of distinct values of
yhhykto grouping by mandt, ktopl and yhkonto
You want this query:
select y.mandt, y.ktopl, y.yhkonto,
count(distinct y.yhhykto)
from yh010 y
group by y.mandt, y.ktopl, y.yhkonto;
Your actual results and resulting query don't match the description, leaving me a bit confused.

Antonio,
Could you please test following SELECT statement with CTE expression
with cte as (
select
distinct mandt, ktopl, yhkonto, yhhykto
from TBL1
)
select
TBL1.*, (
select count(cte.yhhykto)
from cte
where
TBL1.mandt = cte.mandt and
TBL1.ktopl = cte.ktopl and
TBL1.yhkonto = cte.yhkonto
) as cnt
from TBL1

Related

Left join in sql table?

I have the following SQL Server table:
+-------+----------+----------+----------+
| group | subgroup | position | value |
+-------+----------+----------+----------+
| D924 | A | 50 | 9144142 |
| D924 | A | 52 | 9268118 |
| D924 | A | 60 | 9144588 |
| D924 | A | 70 | 10116006 |
| D924 | A | 110 | 9074177 |
| D924 | A | 171 | 7367052 |
| D924 | A | 180 | 10118595 |
| D924 | A | 190 | 9074522 |
| D924 | B | 150 | 12423396 |
| D955 | ... | ... | ... |
+-------+----------+----------+----------+
I need to list all the position for every subgroup within the same group
Like so:
+-------+----------+----------+----------+
| group | subgroup | position | value |
+-------+----------+----------+----------+
| D924 | A | 50 | 9144142 |
| D924 | A | 52 | 9268118 |
| D924 | A | 60 | 9144588 |
| D924 | A | 70 | 10116006 |
| D924 | A | 110 | 9074177 |
| D924 | A | 171 | 7367052 |
| D924 | A | 180 | 10118595 |
| D924 | A | 190 | 9074522 |
| D924 | A | 150 | |
| D924 | B | 50 | |
| D924 | B | 52 | |
| D924 | B | 60 | |
| D924 | B | 70 | |
| D924 | B | 110 | |
| D924 | B | 171 | |
| D924 | B | 180 | |
| D924 | B | 190 | |
| D924 | B | 150 | 12423396 |
| D955 | ... | ... | ... |
+-------+----------+----------+----------+
I would like to achieve the result table in a single SQL query. Can you advise?
This is simply a DISTINCT list of the position and [Group] & subgroup values with a LEFT JOIN back to the table.
Doing a 2 DISTINCT queries will be expensive, so if you have a table of your groups and positions, I would suggest using those, rather than the CTEs:
WITH Groups AS
(SELECT DISTINCT
[group],
subgroup
FROM dbo.YourTable),
Positions AS
(SELECT DISTINCT
position
FROM dbo.YourTable)
SELECT G.[Group],
G.subgroup,
P.Position,
YT.[value]
FROM Groups G
CROSS JOIN Positions P
LEFT JOIN dbo.YourTable YT ON G.[Group] = YT.[Group]
AND G.subgroup = YT.subgroup
AND P.Position = YT.Position;
You seem to only want the value for the first "position" in each group. That suggests row_number():
select group, subgroup, position,
(case when row_number() over (partition by group, position order by subgroup) = 1
then value
end) as value
from t;
Here is a db<>fiddle.
Select distinct subgroups and positions first, then join them and outer join your table.
with sub as (select distinct group, subgroup from mytable)
, pos as (select distinct group, position from mytable)
select
sub.group. sub.subgroup, pos.position, t.value
from sub
join pos on pos.group = sub.group
left join mytable t on t.group = sub.group
and t.subgroup = sub.subgroup
and t.position = pos.position
order by t.group, t.subgroup, t.position;

Calculating sum of children's balance for each parent

I have two tables having parent-child relationship
Table1
| Account_no | Main_account_no | bla bla
+------------+-----------------+----------
| 234 | 111 |
| 235 | 111 |
| 222 | 112 |
| 220 | 112 |
The second:
Table2
| Account_no | Balance | bla bla
+------------+-----------------+----------
| 234 | 10 |
| 235 | 15 |
| 222 | 55 |
| 220 | 45 |
What I need is to calculate the sum of balance of child accounts for each parent account.
The expected result is
| Main_account_no | Balance |
+-----------------+---------+
| 111 | 35 |
| 112 | 100 |
I am trying to use
SELECT MAIN_ACCOUNT_NO, SUM((SELECT BALANCE FROM TABLE2 WHERE ACCOUNT_NO = A.ACCOUNT_NO)) OVER (PARTITION BY MAIN_ACCOUNT_NO)
FROM TABLE1 A
GROUP BY MAIN_ACCOUNT_NO
But I get the ORA-00979: not a GROUP BY expression
Where did I do wrong?
Since this apparently isn't homework... :-)
First let's get the main_account_nos alongside the balances:
select
Table1.Main_account_no,
Table1.Account_no,
Table2.Balance
from
Table1
join Table2 on Table1.Account_no = Table2.Account_no
Gives
| Main_account_no | Account_no | Balance |
+-----------------+------------+-----------------+
| 111 | 234 | 10 |
| 111 | 235 | 15 |
| 112 | 222 | 55 |
| 112 | 220 | 45 |
Now we can easily group as required:
select
Table1.Main_account_no,
sum(Table2.Balance) as total
from
Table1
join Table2 on Table1.Account_no = Table2.Account_no
group by
Table1.Main_account_no

Transpose data using sql oracle

I have following data in a table
+---------+-----+----------+
| Zip_ cd | id | assignmt.|
|---------+-----+----------+
| 1812 | 777 | S |
| 1812 | 111 | P |
| 1451 | 878 | S |
| 55 | 45 | x |
| 55 | 646 | T |
| 55 | 455 | Z |
+---------+-----+----------+
I want to transpose it as following
+---------+-----+----+---------+-----+----------+---------+-----+----------+
| Zip_ cd | id | ass| Zip_cd1 | id1 |assignmt1 | Zip_cd2 | id2 |assignmt3 |
+---------+-----+----+---------+-----+----------+---------+-----+----------+
| 1812 | 777 | S | 1812 | 111 | P | 1812 | 111 | P |
| 1451 | 878 | S | | | | | | |
| 55 | 45 | X | 55 | 646 | T | 55 | 455 | Z |
+---------+-----+----------+---------+----------+---------+-----+----------+
So, I basically want to transpose based on the zip code. If 2 rows have same zip code its needs to be in single row.
Another query using pivot functionality:
select *
from table_name
pivot ( max(id) id
, max(assignmt) assignmt
FOR assignmt IN ('S' S,
'P' P));
+---------+-------+------------+--------+------------+
| ZIP_ CD | S_ID | S_ASSIGNMT.| P_ID | P_ASSIGNMT.|
+---------+-------+------------+--------+------------+
| 1812 | 777 | S | 111 | P |
| 1451 | 878 | S | NULL | NULL |
+---------+-------+------------+--------+------------+

SQL Group Results and Add them up based on product

I've got the following SQL which basically works out the costings etc for each item.
SELECT
L.LocID,
L.LocationName,
L.LocationSqrMtr,
L.LocationAddress,
L.LocationPostCode,
L.LocationContact,
I.SubPIDItemID,
I.SPID,
I.ProductID,
C.SubPIDCostID,
C.PricePerItem,
C.ManDayPerItem
FROM
dbo.SubPIDCosts AS C
INNER JOIN dbo.SubPIDItems AS I ON
C.ProductID = I.ProductID
RIGHT OUTER JOIN dbo.SubPIDLocations AS L ON
I.LocationID = L.LocID AND C.LocationID = L.LocID
WHERE C.SPID = 48
This returns:
+-------+--------------+----------------+-----------------+------------------+-----------------+--------------+----------+-----------+--------------+--------------+---------------+-----+----+---+-----+--------+--------+--------+------+
| LocID | LocationName | LocationSqrMtr | LocationAddress | LocationPostCode | LocationContact | SubPIDItemID | SPID | ProductID | SubPIDCostID | PricePerItem | ManDayPerItem | | | | | | | | |
+-------+--------------+----------------+-----------------+------------------+-----------------+--------------+----------+-----------+--------------+--------------+---------------+-----+----+---+-----+--------+--------+--------+------+
| 379 | | | Location | 1 | 1 | 345 | Generic | Building | Generic | Building | NULL | 158 | 48 | | | | 108 | 0.3400 | 6.17 |
| 379 | | | Location | 1 | 1 | 345 | Generic | Building | Generic | Building | NULL | 159 | 48 | 1 | 109 | 0.3400 | | .47 | |
| 379 | 3 | Location | 1 | 615 | Generic | Building | Generic | Building | NULL | 160 | 48 | | | | 110 | 0.7317 | 0.50 | | |
| 379 | 4 | Location | | | 615 | Generic | Building | Generic | Building | NULL | 161 | 48 | | | | 111 | 0.7317 | 0.50 | |
| 379 | 4 | Location | | | 615 | Generic | Building | Generic | Building | NULL | 16 | | 48 | 1 | 11 | | 0.7317 | 0.50 | |
+-------+--------------+----------------+-----------------+------------------+-----------------+--------------+----------+-----------+--------------+--------------+---------------+-----+----+---+-----+--------+--------+--------+------+
But what I would like to do is group it by the product ID. So all the ones that are ProductID 22 it should add the values up for ManDayPerItem and PricePerItem and LocationSqrMtr
It's used in the following context
I'd like to have them grouped nicely in that list.
As you said, group by:
SELECT ProductID, SUM(ManDayPerItem), SUM(PricePerItem), SUM(LocationSqrMtr)
FROM
dbo.SubPIDCosts AS C
INNER JOIN dbo.SubPIDItems AS I ON
C.ProductID = I.ProductID
RIGHT OUTER JOIN dbo.SubPIDLocations AS L ON
I.LocationID = L.LocID AND C.LocationID = L.LocID
WHERE C.SPID = 48
GROUP BY ProductID;

Adding rows and changing the row name

I have fetched the values from the sql server database from the following code,
SELECT [Zone Name]
,[Zone Count]
,[Phase Name]
FROM [Interface].[dbo].[VwZoneCount]
where [Zone Name] IN ('EB2GFNMZ','EB2GFSMZ','EB2GFNZ1','EB2GFSZ1','EB21FNZ1','EB21FSMZ','EB2IFSZ1','EB22FNZ1','EB22FSZ1','EB22FSMZ','EB23FNMZ','EB23FNZ1','EB23FNZ2','EB23FNZ3','EB23FSMZ','EB23FSZ1','EB23FSZ2','EB24FNMZ','EB24FNZ1','EB24FSMZ','EB24FSZ1','EB25FNMZ','EB25FNZ1','EB25FSMZ','EB25FSZ1','EB26FNMZ','EB26FNZ1','EB26FSMZ','EB26FSZ1','EB27FNZ1','EB27FSMZ')
GO
The ouput for the above query is ,
Zone Name Zone Count
EB24FNZ1 160
EB24FSMZ 10
EB24FSZ1 87
EB25FNMZ 82
EB25FNZ1 82
EB25FSMZ 12
EB25FSZ1 123
EB26FNMZ 4
EB26FNZ1 92
EB26FSMZ 23
EB26FSZ1 91
EB27FNZ1 1
EB27FSMZ 64
EB2GFNMZ 12
EB2GFNZ1 152
EB2GFSMZ 36
EB2GFSZ1 212
but i need the output by summing some row values .I need to combine values that have 'EB2GFN%' to one with different name,'EB21FN%'..similarly need to combine other rows. Can any body suggest me how i would do that .
Desired output:-
Zone Name Zone Count
EB24F_NORTH_WING 160
EB24F_SOUTH_WING 10+87
EB25F_NORTH_WING 82+82
EB25F_SOUTH_WING 12+123
EB26F_NORTH_WING 4+92
EB26F_SOUTH_WING 23+91
EB27F_NORTH_WING 1
EB27F_SOUTH_WING 64
EB2GF_NORTH_WING 12+152
EB2GF_SOUTH_WING 36+212
You can do this:
;WITH AllZones
AS
(
SELECT * FROM YourQuery
), WithGroupedZones
AS
(
SELECT
ZoneName,
ZoneCount,
LEFT(ZoneName, 2) Eb,
SUBSTRING(ZoneName, 3, 1) EbNumber,
SUBSTRING(ZoneName, 4, 3) F,
SUBSTRING(ZoneName, 8, 1) FNumber
FROM AllZones
)
SELECT
ZoneName,
(SELECT SUM(t2.ZoneCount)
FROM WithGroupedZones t2
WHERE t1.Eb = t2.Eb
AND t1.F = t2.F
AND t1.EBNumber= t2.EBnumber
) ZonesCount
FROM WithGroupedZones t1;
SQL Fiddle Demo
This will give you:
| ZONENAME | ZONESCOUNT |
-------------------------
| EB24FNZ1 | 160 |
| EB24FSMZ | 97 |
| EB24FSZ1 | 97 |
| EB25FNMZ | 164 |
| EB25FNZ1 | 164 |
| EB25FSMZ | 135 |
| EB25FSZ1 | 135 |
| EB26FNMZ | 96 |
| EB26FNZ1 | 96 |
| EB26FSMZ | 114 |
| EB26FSZ1 | 114 |
| EB27FNZ1 | 1 |
| EB27FSMZ | 64 |
| EB2GFNMZ | 164 |
| EB2GFNZ1 | 164 |
| EB2GFSMZ | 248 |
| EB2GFSZ1 | 248 |
Note that: This might be not the same result set that you are looking for. But you can modify the condition, I used in my query:
t1.Eb = t2.Eb
AND t1.F = t2.F
AND t1.EBNumber= t2.EBnumber
To get your desired output. Also note that the zones' names are grouped into:
| ZONENAME | ZONECOUNT | EB | EBNUMBER | F | FNUMBER |
--------------------------------------------------------
| EB24FNZ1 | 160 | EB | 2 | 4FN | 1 |
| EB24FSMZ | 10 | EB | 2 | 4FS | Z |
| EB24FSZ1 | 87 | EB | 2 | 4FS | 1 |
| EB25FNMZ | 82 | EB | 2 | 5FN | Z |
| EB25FNZ1 | 82 | EB | 2 | 5FN | 1 |
| EB25FSMZ | 12 | EB | 2 | 5FS | Z |
| EB25FSZ1 | 123 | EB | 2 | 5FS | 1 |
| EB26FNMZ | 4 | EB | 2 | 6FN | Z |
| EB26FNZ1 | 92 | EB | 2 | 6FN | 1 |
| EB26FSMZ | 23 | EB | 2 | 6FS | Z |
| EB26FSZ1 | 91 | EB | 2 | 6FS | 1 |
| EB27FNZ1 | 1 | EB | 2 | 7FN | 1 |
| EB27FSMZ | 64 | EB | 2 | 7FS | Z |
| EB2GFNMZ | 12 | EB | 2 | GFN | Z |
| EB2GFNZ1 | 152 | EB | 2 | GFN | 1 |
| EB2GFSMZ | 36 | EB | 2 | GFS | Z |
| EB2GFSZ1 | 212 | EB | 2 | GFS | 1 |
Then you can compare the ZoneName using these groups EB, EBNUMBER, F, FNUMBER
Try this, it will give you sums for groups that have first 6 letters in common:
SELECT LEFT(Zone_Prefix, 5) + CASE WHEN RIGHT(Zone_Prefix, 1) = 'N' THEN '_NORTH_WING' ELSE '_SOUTH_WING' END AS [Zone Name],
Cnt AS [Zone Count]
FROM
(
SELECT LEFT([Zone Name], 6) AS Zone_Prefix
,SUM([Zone Count]) Cnt
FROM [Interface].[dbo].[VwZoneCount]
WHERE [Zone Name] IN ('EB2GFNMZ','EB2GFSMZ','EB2GFNZ1','EB2GFSZ1','EB21FNZ1','EB21FSMZ','EB2IFSZ1','EB22FNZ1','EB22FSZ1','EB22FSMZ','EB23FNMZ','EB23FNZ1','EB23FNZ2','EB23FNZ3','EB23FSMZ','EB23FSZ1','EB23FSZ2','EB24FNMZ','EB24FNZ1','EB24FSMZ','EB24FSZ1','EB25FNMZ','EB25FNZ1','EB25FSMZ','EB25FSZ1','EB26FNMZ','EB26FNZ1','EB26FSMZ','EB26FSZ1','EB27FNZ1','EB27FSMZ')
GROUP BY
LEFT([Zone Name], 6)
) tbl
Here is an SQL Fiddle