how to do sum with multiple joins in PostgreSQL? - sql

I know that my question would be duplicated but I really don't know how to created sql which return results of sum with multiple join.
Tables I have
result_summary
num_bin id_summary count_bin
3 172 0
4 172 0
5 172 0
6 172 0
7 172 0
8 172 0
1 174 1
2 174 0
3 174 0
4 174 0
5 174 0
6 174 0
7 174 0
8 174 0
1 175 0
summary_assembly
num_lot id_machine sabun date_work date_write id_product shift count_total count_fail count_good id_summary id_operation
adfe 1 21312 2020-11-25 2020-11-25 1 A 10 2 8 170 2000
adfe 1 21312 2020-11-25 2020-11-25 1 A 1000 1 999 171 2000
adfe 1 21312 2020-11-25 2020-11-25 2 A 100 1 99 172 2000
333 1 21312 2020-12-06 2020-12-06 1 A 10 2 8 500 2000
333 1 21312 2020-11-26 2020-11-26 1 A 10000 1 9999 174 2000
333 1 21312 2020-11-26. 2020-11-26 1 A 100 0 100 175 2000
333 1 21312 2020-12-06 2020-12-06 1 A 10 2 8 503 2000
333 1 21312 2020-12-07 2020-12-07 1 A 10 2 8 651 2000
333 1 21312 2020-12-02 2020-12-02 1 A 10 2 8 178 2000
employees
sabun name_emp
3532 Kim
12345 JS
4444 Gilsoo
21312 Wayn Hahn
123 Lee too
333 JD
info_product
id_product name_product
1 typeA
2 typeB
machine
id_machine id_operation name_machine
1 2000 name1
2 2000 name2
3 2000 name3
4 3000 name1
5 3000 name2
6 3000 name3
7 4000 name1
8 4000 name2
query
select S.id_summary, I.name_product, M.name_machine,
E.name_emp, S.sabun, S.date_work,
S.shift, S.num_lot, S.count_total,
S.count_good, S.count_fail,
sum(case num_bin when '1' then count_bin else 0 end) as bin1,
sum(case num_bin when '2' then count_bin else 0 end) as bin2,
sum(case num_bin when '3' then count_bin else 0 end) as bin3,
sum(case num_bin when '4' then count_bin else 0 end) as bin4,
sum(case num_bin when '5' then count_bin else 0 end) as bin5,
sum(case num_bin when '6' then count_bin else 0 end) as bin6,
sum(case num_bin when '7' then count_bin else 0 end) as bin7,
sum(case num_bin when '8' then count_bin else 0 end) as bin8
from result_assembly as R
join summary_assembly as S on R.id_summary = S.id_summary
join employees as E on S.sabun = E.sabun
join info_product as I on S.id_product = I.id_product
join machine as M on S.id_machine = M.id_machine
where I.id_product = '1'
and E.sabun='21312'
and S.shift = 'A'
and S.date_work between '2020-11-10' and '2020-12-20'
group by S.id_summary, E.name_emp, S.num_lot,
I.name_product,M.name_machine
order by S.id_summary;
result
id_summary name_product name_machine name_emp sabun date_work shift num_lot count_total count_good count_fail bin1 bin2 bin3 bin4 bin5 bin6 bin7 bin8
170 TypeA name1 Kim 21312 2020-11-25 A adfe 10 8 2 1 1 0 0 0 0 0 0
171 TypeA name1 Kim 21312 2020-11-25 A adfe 1000 999 1 1 1 0 0 0 0 0 0
174 TypeA name1 Kim 21312 2020-11-26 A 333 10000 9999 1 1 1 0 0 0 0 0 0
175 TypeA name1 Kim 21312 2020-11-26 A 333 100 100 0 0 0 0 0 0 0 0 0
178 TypeA name1 Kim 21312 2020-12-02 A 333 10 8 2 1 1 0 0 0 0 0 0
179 TypeA name1 Kim 21312 2020-12-02 A 333 10 8 2 1 1 0 0 0 0 0 0
180 TypeA name1 Kim 21312 2020-12-02 A 333 10 8 2 1 1 0 0 0 0 0 0
181 TypeA name1 Kim 21312 2020-12-02 A 333 10 8 2 1 1 0 0 0 0 0 0
182 TypeA name2 Kim 21312 2020-12-02 A 333 10 8 2 1 1 0 0 0 0 0 0
186 TypeA name2 Kim 21312 2020-12-06 A 333 10 8 2 1 1 0 0 0 0 0 0
193 TypeA name2 Kim 21312 2020-12-06 A 333 10 8 2 0 0 0 0 0 0 0 0
194 TypeA name2 Kim 21312 2020-12-06 A 333 10 8 2 0 0 0 0 0 0 0 0
195 TypeA name2 Kim 21312 2020-12-06 A 333 10 8 2 0 0 0 0 0 0 0 0
196 TypeA name2 JS 21312 2020-12-06 A 333 10 8 2 0 0 0 0 0 0 0 0
197 TypeA name2 JS 21312 2020-12-06 A 333 10 8 2 0 0 0 0 0 0 0 0
198 TypeA name2 JS 21312 2020-12-06 A 333 10 8 2 0 0 0 0 0 0 0 0
199 TypeA name2 JS 21312 2020-12-06 A 333 10 8 2 0 0 0 0 0 0 0 0
200 TypeA name2 JS 21312 2020-12-06 A 333 10 8 2 0 0 0 0 0 0 0 0
expected output(when sum by num_lot)
num_lot count_total count_good count_fail bin1 bin2 bin3 bin4 bin5 bin6 bin7 bin8
adfe 323 300 23 22 1 0 0 0 0 0 0
333 4312 4300 12 10 2 0 0 0 0 0 0
All of them were modified from original one because they were non-English, so there would be typo.
Here now I need to sum by num_lot, name_product or sabun.
id_summary is unique.
Thanks

As expected in the comments: It seems like you simple need a subquery which groups your table by the column num_lot
SELECT
num_lot,
SUM(count_total),
SUM(count_good)
-- some more SUM()
FROM (
--<your query>
) s
GROUP BY num_lot
It was asked in the comments what the s stands for: A subquery needs an alias, an identifier. Because I didn't want to think about a better name, I just called the subselect s. It is the shortcut for AS s

It sounds like you want to use crosstab() -- https://www.postgresql.org/docs/current/tablefunc.html

Related

SQL: Increment a row when value in another row changes

I have the following table:
Sequence Change
100 0
101 0
103 0
106 0
107 1
110 0
112 1
114 0
115 0
121 0
126 1
127 0
134 0
I need an additional column, Group, whose values increment based on the occurrence of 1 in Change. How is that done? I'm using Microsoft Server 2012.
Sequence Change Group
100 0 0
101 0 0
103 0 0
106 0 0
107 1 1
110 0 1
112 1 2
114 0 2
115 0 2
121 0 2
126 1 3
127 0 3
134 0 3
You want a cumulative sum:
select t.*, sum(change) over (order by sequence) as grp
from t;

SQL Server 2016 incorrect query plan estimate despite updated statistic

I'm in the middle of optimizing a query and notice that it becomes really slow because it estimated the number of rows to be 16.6 and the actual number of rows being returned is 565824. I updated the statistic, dropped and recreated but it still gives the incorrect estimate. This is for SQL Server 2016, any help is appreciated.
SQL:
select cd_key
from dbo.CAMPDIV
where cd_camp = 'a'
and CD_CAMPYR = '2018'
option (recompile)
Histogram for nonclustered index (cd_campyr)
All Density Average Length Columns
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0.02040816 4 CD_CAMPYR
7.412665E-08 8 CD_CAMPYR, CD_ID
7.184833E-08 18 CD_CAMPYR, CD_ID, CD_CAMP
Histogram Steps
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0 792181 0 1
1979 0 230 0 1
1980 0 332 0 1
1981 0 604 0 1
1982 0 622 0 1
1983 0 330 0 1
1984 0 1762 0 1
1985 0 868 0 1
1986 0 551 0 1
1987 0 190 0 1
1988 0 352 0 1
1989 0 519 0 1
1990 0 38829 0 1
1991 0 439486 0 1
1992 0 366357 0 1
1993 0 375469 0 1
1994 0 369176 0 1
1995 0 367691 0 1
1996 0 376979 0 1
1997 0 388239 0 1
1998 0 391408 0 1
1999 0 402551 0 1
2000 0 413392 0 1
2001 0 422470 0 1
2002 0 461895 0 1
2003 0 458726 0 1
2004 0 459876 0 1
2005 0 473357 0 1
2006 0 464213 0 1
2007 0 472373 0 1
2008 0 457623 0 1
2009 0 462268 0 1
2010 0 465633 0 1
2011 0 470338 0 1
2012 0 472091 0 1
2013 0 481586 0 1
2014 0 484236 0 1
2015 0 492460 0 1
2016 0 514569 0 1
2017 0 551739 0 1
2018 0 571969 0 1
2019 0 552550 0 1
2020 0 54 0 1
2021 0 33 0 1
2022 0 21 0 1
2023 0 8 0 1
2025 1 1 1 1
2099 0 1 0 1
It is a bit strange what you tell but in any case... a covering index may help or even produce a drastic increase.
Please, try to create the index:
CREATE INDEX IX_CampDiv_CD_Camp_CD_CampYR ON dbo.CAMPDIV (cd_camp, CD_CAMPYR )
INCLUDE (cd_key)
At least will prevent the Nested Loop what improves the plan.
Please, share the results.

Creating a string of combined values from a SQL Server table

I built a SQL query which returns the following results:
ID Number ID IndexColumn String_To_Use Checking_ID
0000 1 0000 1 -2
1000 2 1000 2 -2
1020 3 1020 3 -2
1130 4 1130 4 -2
1198 5 NULL 9999 NULL NULL
1199 6 1199 5 -2
1210 7 1210 6 -2
1240 8 NULL 9999 NULL NULL
1250 9 NULL 9999 NULL NULL
1260 10 1260 7 7
1261 11 NULL 9999 NULL NULL
1280 12 NULL 9999 NULL NULL
1296 13 NULL 9999 NULL NULL
1298 14 NULL 9999 NULL NULL
1299 15 1299 8 8
1501 16 NULL 9999 NULL NULL
I need to populate the column "String_To_Use" with "ID" values in such a way that If "Checking_ID" column has values -2 more than once repeating (it means user chose IDs in a range), these repeating values would be displayed as "0000-1130"; if values -2 is not being repeated, then for example "1260".
Based on this logic, the above table will contain the following values in the String_To_Use column:
ID Number ID IndexColumn String_To_Use Checking_ID
0000 1 0000 1 0000-1130 -2
1000 2 1000 2 0000-1130 -2
1020 3 1020 3 0000-1130 -2
1130 4 1130 4 0000-1130 -2
1198 5 NULL 9999 NULL NULL
1199 6 1199 5 0000-1210 -2
1210 7 1210 6 0000-1210 -2
1240 8 NULL 9999 NULL NULL
1250 9 NULL 9999 NULL NULL
1260 10 1260 7 1260 7
1261 11 NULL 9999 NULL NULL
1280 12 NULL 9999 NULL NULL
1296 13 NULL 9999 NULL NULL
1298 14 NULL 9999 NULL NULL
1299 15 1299 8 1299 8
1501 16 NULL 9999 NULL NULL
thank you!!
You need to define groups of "adjacency". In this case, you can simply do a cumulative sum of the number of times that checking_id is not -2.
After that, the rest is window functions and string manipulation:
select t.*,
(case when checking_id <> -2
then min(id) over (partition by grp) + '-' + max(id) over (partition by grp)
else id
end) as string_to_use
from (select t.*,
sum(case when checking_id <> -2 then 1 else 0 end) over (order by id) as grp
from t
) t;
This version assumes that id is a string. If it is a number, the code is easily adapted by cluttering it with cast() or convert().
select t.*,
(case when Checking_id = -2
then min(id) over (partition by grp) + '-' + max(id) over (partition by grp)
else id
end) as string_to_use
from (select t.*
,sum(case when Checking_id = -2 then 1 else 0 end) over (partition by id) as grp
from t
) t order by id;
ID Number ID IndexColumn String_To_Use Checking_id grp string_to_use
0000 1 0000 1 -2 1 0000 -1210
1000 2 1000 2 -2 1 0000 -1210
1020 3 1020 3 -2 1 0000 -1210
1130 4 1130 4 -2 1 0000 -1210
1198 5 NULL 9999 NULL NULL 0 NULL
1199 6 1199 5 -2 1 0000 -1210
1210 7 1210 6 -2 1 0000 -1210
1240 8 NULL 9999 NULL NULL 0 NULL
1250 9 NULL 9999 NULL NULL 0 NULL
1260 10 1260 7 7 0 1260
1261 11 NULL 9999 NULL NULL 0 NULL
1280 12 NULL 9999 NULL NULL 0 NULL
1296 13 NULL 9999 NULL NULL 0 NULL
1298 14 NULL 9999 NULL NULL 0 NULL
1299 15 1299 8 8 0 1299
1501 16 NULL 9999 NULL NULL 0 NULL

SQL aggregation query and sum columns

I have this table (I put the name over needed colums)
iddip date idv idc val
47 2018-06-01 00:00:00.000 0 3 3 60 NULL NULL
47 2018-06-01 00:00:00.000 0 1 3 200 NULL NULL
47 2018-06-01 00:00:00.000 0 1 4 280 NULL NULL
43 2018-06-01 00:00:00.000 0 3 2 510 NULL NULL
53 2018-06-01 00:00:00.000 0 1 4 480 NULL NULL
29 2018-06-01 00:00:00.000 0 3 2 510 NULL NULL
2 2018-06-11 00:00:00.000 0 1 2 480 NULL NULL
47 2018-06-02 00:00:00.000 0 1 3 100 NULL NULL
I want to obtain this:
id idc Totidv1 Totidv3 TOT
47 3 300 60 360
47 4 280 0 280
43 2 0 510 510
53 4 480 0 480
29 2 0 510 510
2 2 480 0 480
The closest I can get is:
SELECT DISTINCT(iddip),IDCENTROCOSTO,tot=SUM(VALORE),ord=( SELECT SUM(isnull(VALORE,0)) FROM VALORIVOCICDC WHERE IDVOCE='1' and iddip=v.IDDIP and IDCENTROCOSTO ='3' GROUP BY iddip,IDCENTROCOSTO),
str=( SELECT SUM(isnull(VALORE,0)) FROM VALORIVOCICDC WHERE IDVOCE='3' and iddip=v.IDDIP and IDCENTROCOSTO ='3' GROUP BY iddip,IDCENTROCOSTO)
FROM VALORIVOCICDC v
GROUP BY v.iddip,IDCENTROCOSTO
But it returns wrong sums in totidv1 and totisv3, How can I do this? Thanks for any hint
You just need a GROUP BY here (not distinct) and a couple of CASE statements:
SELECT
id,
idc,
SUM(CASE WHEN idv=3 THEN idv ELSE 0 END) as totidv1,
SUM(CASE WHEN idv=1 THEN idv ELSE 0 END) as totidv3,
SUM(idv) as Tot
FROM yourtable
GROUP BY id, idc
Note that Distinct is not a function that you can call like SELECT DISTINCT(somecolumn) This is functionally equivalent to SELECT DISTINCT somecolumn... in that it works against the entire record set returned by the SELECT statement either way.

Count and SUM using case

I have table like this, name: Table.dbo
Amount Desc Month SM code ID
$32,323.00 Bla1 1 121 3 2424221
$4,242.00 Bla2 1 A1 3 2424221
$3,535.00 Bla3 1 A3 1 3230824
$4,984.00 Bla4 1 433 1 3230824
$47,984.00 Bla5 1 B1 1 3230824
$3,472.00 Bla6 1 D2 27 2297429
$3,239.00 Bla7 1 124 27 2297429
$4,249.00 Bla8 1 114 24 3434334
$2,492.00 Bla9 1 132 24 3434334
$424.00 Bla10 2 232 3 2424221
$24,242.00 Bla7 2 124 3 2424221
$242,424 Bla4 2 433 1 3230824
$533.00 Bla13 2 235 1 3230824
$4,342.00 Bla14 2 223 1 3230824
$24,242.00 Bla15 2 224 27 2297429
$24,242.00 Bla1 2 121 27 2297429
$4,242.00 Bla17 2 432 24 3434334
$24,224.00 Bla9 2 132 24 3434334
I wrote this query :
select
[SM],
count(*) as TotalCntOfSM,
sum(case when [code] between 4 and 27 then 1 else 0 end) as TotalCntOfSM_R,
sum(case when [code] in (1,2,3) then 1 else 0 end) as TotalCntOfSM_B,
sum(case when [code] in (1) then 1 else 0 end) as TotalCntofSM_B1,
sum(case when [code] in (2) then 1 else 0 end) as TotalCntofSM_B2,
sum(case when [code] in (3) then 1 else 0 end) as TotalCntofSM_B3,
sum([Amount]) As TotalAmount
****[How can I sum the Amount for the SM if the code is between 4 and 27?** For example]**
from [Table]
group by [SM]
order by TotalCntOfSM desc
How can I sum the Amount for the SM if the code is between 4 and 27 or the code is in (1,2,3) only (For example).
Thank you very much!
Exactly like qxg said - Replace
****[How can I sum the Amount for the SM if the code is between 4 and 27?** For example]** `
with
sum(case when [code] between 4 and 27 then [Amount] else 0 end) as SMAmount
If you want to total up amount for code between 4 and 27 or for codes 1,2,3
sum(case when [code] between 1 and 27 then [Amount] else 0 end) as SMAmount
You can write the above also as
sum(case when [code] between 4 and 27 OR [code] in (1,2,3) then [Amount] else 0 end) as SMAmount