Rails Query - Group By with 2 groups - sql

In my website my users have an attribute localidade. This specifies where they live.
I'm trying to do a query where I group the results the following way:
localidade | Number of Users
-------------+--------------
New York | 6
Not New York | 8
I want the number of users from New York and the number of users from anywhere else but New York.
I tried this:
User.group("lower(localidade) = 'new york'").count
but since I don't have any users from new york and only 1 not from New York it returns:
{false => 1}
Am I able to give aliases to groups? Is there any way of grouping this way the results?
I'm gonna use the results for a Pie Graph from Graphkick.

You could write your query :
User.group("lower(localidade)")
.select("CASE WHEN lower(localidade) = 'new york' THEN COUNT(id) END AS NewYork,
CASE WHEN lower(localidade) != 'new york' THEN COUNT(id) END AS Non-NewYork")
Since 9.4, you can use FILTER with aggregate expression :
User.group("lower(localidade)")
.select("COUNT(id) FILTER (WHERE lower(localidade) != 'new york') AS NonNewyork,
COUNT(id) FILTER (WHERE lower(localidade) = 'new york') AS Newyork")
I created a Table to explain and test the above sql, and they worked as expected :
[shreyas#rails_app_test (master)]$ rails db
psql (9.4.1)
Type "help" for help.
app_development=# select id, location, name from people;
id | location | name
----+----------+------
2 | X | foo
3 | X | foo
4 | Y | foo
(3 rows)
app_development=# SELECT COUNT(id) FILTER(WHERE lower(location) != 'x') AS Non_X_loc, COUNT(id) FILTER (WHERE lower(location) = 'x') AS X_loc FROM "people";
non_x_loc | x_loc
-----------+-------
1 | 2
(1 row)
Let me now, jump to the rails console, and test the equivalent Rails code :
[2] pry(main)> p = Person.select("COUNT(id) FILTER(WHERE lower(location) != 'x') AS Non_X_loc, COUNT(id) FILTER (WHERE lower(location) = 'x') AS X_loc ")
Person Load (0.5ms) SELECT COUNT(id) FILTER(WHERE lower(location) != 'x') AS Non_X_loc, COUNT(id) FILTER (WHERE lower(location) = 'x') AS X_loc FROM "people"
=> [#<Person:0x007fd85ed71980 id: nil>]
[3] pry(main)> p.first.attributes
=> {"id"=>nil, "non_x_loc"=>1, "x_loc"=>2}
[6] pry(main)> Person.group("lower(location)").select("CASE WHEN lower(location) = 'x' THEN COUNT(id) END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) END AS Non_X_loc")
Person Load (0.6ms) SELECT CASE WHEN lower(location) = 'x' THEN COUNT(id) END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) END AS Non_X_loc FROM "people" GROUP BY lower(location)
=> [#<Person:0x007fd8608281e8 id: nil>, #<Person:0x007fd860828008 id: nil>]
[7] pry(main)> p = _
=> [#<Person:0x007fd8608281e8 id: nil>, #<Person:0x007fd860828008 id: nil>]
[8] pry(main)> p.map { |rec| rec.attributes }
=> [{"id"=>nil, "x_loc"=>nil, "non_x_loc"=>1}, {"id"=>nil, "x_loc"=>2, "non_x_loc"=>nil}]
[9] pry(main)> p.map { |rec| rec.attributes.except('id') }
=> [{"x_loc"=>nil, "non_x_loc"=>1}, {"x_loc"=>2, "non_x_loc"=>nil}]
Update
You can remove those nil from DB level only :
Rails code :
[shreyas#rails_app_test (master)]$ rails c
Loading development environment (Rails 4.2.0)
[1] pry(main)> Person.group("lower(location)").select("CASE WHEN lower(location) = 'x' THEN COUNT(id) ELSE 0 END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) ELSE 0 END AS Non_X_loc")
Person Load (0.9ms) SELECT CASE WHEN lower(location) = 'x' THEN COUNT(id) ELSE 0 END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) ELSE 0 END AS Non_X_loc FROM "people" GROUP BY lower(location)
=> [#<Person:0x007fd858c100b0 id: nil>, #<Person:0x007fd860853e88 id: nil>]
[2] pry(main)> p = _
=> [#<Person:0x007fd858c100b0 id: nil>, #<Person:0x007fd860853e88 id: nil>]
[3] pry(main)> p.map { |rec| rec.attributes }
=> [{"id"=>nil, "x_loc"=>0, "non_x_loc"=>1}, {"id"=>nil, "x_loc"=>2, "non_x_loc"=>0}]
[4] pry(main)> p.map { |rec| rec.attributes.except('id') }
=> [{"x_loc"=>0, "non_x_loc"=>1}, {"x_loc"=>2, "non_x_loc"=>0}]
[5] pry(main)> p = Person.select("count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc").group("lower(location)")
Person Load (0.9ms) SELECT count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc FROM "people" GROUP BY lower(location)
=> [#<Person:0x007fd85b150f78 id: nil>, #<Person:0x007fd85b150230 id: nil>]
[6] pry(main)> p.map { |rec| rec.attributes.except('id') }
=> [{"x_loc"=>0, "non_x_loc"=>1}, {"x_loc"=>2, "non_x_loc"=>0}]
SQL
app_development=# select CASE WHEN lower(location) = 'x' THEN COUNT(id) ELSE 0 END AS X_loc, CASE WHEN lower(location) != 'x' THEN COUNT(id) ELSE 0 END AS Non_X_loc from people group by lower(location);
x_loc | non_x_loc
-------+-----------
0 | 1
2 | 0
(2 rows)
app_development=# select count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc from people group by lower(location);
x_loc | non_x_loc
-------+-----------
0 | 1
2 | 0
(2 rows)
Update- II
The classical approach to get the output same as FILTER :
app_development=# select count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, sum(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc from people;
x_loc | non_x_loc
-------+-----------
2 | 1
(1 row)
app_development=# select sum(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, sum(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc from people;
x_loc | non_x_loc
-------+-----------
2 | 1
(1 row)
app_development=# select id, location, name from people;
id | location | name
----+----------+------
2 | X | foo
3 | X | foo
4 | Y | foo
(3 rows)
app_development=#
And In Rails way :-
Loading development environment (Rails 4.2.0)
[1] pry(main)> p = Person.select("sum(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, sum(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc")
Person Load (0.6ms) SELECT sum(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, sum(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc FROM "people"
=> [#<Person:0x007fd85b6e6a78 id: nil>]
[2] pry(main)> p.first.attributes.except("id")
=> {"x_loc"=>2, "non_x_loc"=>1}
[3] pry(main)> p = Person.select("count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc")
Person Load (0.5ms) SELECT count(CASE WHEN lower(location) = 'x' THEN 1 END) AS X_loc, count(CASE WHEN lower(location) != 'x' THEN 1 END) AS Non_X_loc FROM "people"
=> [#<Person:0x007fd85b77f098 id: nil>]
[4] pry(main)> p.first.attributes.except("id")
=> {"x_loc"=>2, "non_x_loc"=>1}
[5] pry(main)>

Honestly, what you have works fine, you just need to understand that if there's no value in the hash for true (or for false for that matter) then the value must default to zero, you can do that with .to_i on what will be a nil value. So, eg.:
ny_count = User.group("lower(localidade) = 'new york'").count
"New York: #{ny_count[true].to_i}
Not New York: #{ny_count[false].to_i}
"

Related

HANA SQL Performance

Can someone help me to reshape this code in a proper way. This Code is old one and would like to reshape it. But not sure how to do it better.
SELECT COUNT(*) FROM (
SELECT SRC.CLIENT,SRC.GUID
,COALESCE(SRC.PARTNER_ZSCHEDUL,'') PARTNER_ZSCHEDUL
,COALESCE(SRC.PARTNER_ZSVCUNIT,'') PARTNER_ZSVCUNIT
,COALESCE(SRC.PARTNER_ZPARTAPP,'') PARTNER_ZPARTAPP
,COALESCE(SRC.PARTNER_ZESCOWN,'') PARTNER_ZESCOWN
,COALESCE(SRC.PARTNER_ZESCAPRV,'') PARTNER_ZESCAPRV
,COALESCE(SRC.PARTNER_ZESCRQST,'') PARTNER_ZESCRQST
,COALESCE(SRC.PARTNER_ZPARTORD,'') PARTNER_ZPARTORD
,COALESCE(SRC.PARTNER_ER,'') PARTNER_ER
FROM KCP810_HBP.ZCRMD_ORDERADM_I_EXTN TGT, (
SELECT CLIENT,GUID
, MIN(CASE WHEN PARTNER_FCT = 'ZSCHEDUL' THEN PARTNER END) PARTNER_ZSCHEDUL
, MIN(CASE WHEN PARTNER_FCT = 'ZSVCUNIT' THEN MERGED_PARTNER END) PARTNER_ZSVCUNIT
, MIN(CASE WHEN PARTNER_FCT = 'ZSVCUNIT' THEN PARTNER END) PARTNER_ZSVCUNIT_HR
, MIN(CASE WHEN PARTNER_FCT = 'ZPARTAPP' THEN PARTNER END) PARTNER_ZPARTAPP
, MIN(CASE WHEN PARTNER_FCT = 'ZESCOWN' THEN PARTNER END) PARTNER_ZESCOWN
, MIN(CASE WHEN PARTNER_FCT = 'ZESCAPRV' THEN PARTNER END) PARTNER_ZESCAPRV
, MIN(CASE WHEN PARTNER_FCT = 'ZESCRQST' THEN PARTNER END) PARTNER_ZESCRQST
, MIN(CASE WHEN PARTNER_FCT = 'ZPARTORD' THEN PARTNER END) PARTNER_ZPARTORD
, MIN(CASE WHEN PARTNER_FCT = '00000014' THEN PARTNER END) PARTNER_ER
FROM "_SYS_BIC"."CRM-SV.A-COMMON-VIEWS.Proc.Item/CV_IT_01_PARTNER_ALL" GROUP BY CLIENT,GUID ) SRC
WHERE TGT.CLIENT = SRC.CLIENT
AND TGT.GUID = SRC.GUID
AND ( TGT.PARTNER_ZSCHEDUL != SRC.PARTNER_ZSCHEDUL OR TGT.PARTNER_ZSVCUNIT != SRC.PARTNER_ZSVCUNIT OR
TGT.PARTNER_ZPARTAPP != SRC.PARTNER_ZPARTAPP OR TGT.PARTNER_ZESCOWN != SRC.PARTNER_ZESCOWN OR
TGT.PARTNER_ZESCAPRV != SRC.PARTNER_ZESCAPRV OR TGT.PARTNER_ZESCRQST != SRC.PARTNER_ZESCRQST OR
TGT.PARTNER_ZPARTORD != SRC.PARTNER_ZPARTORD OR TGT.PARTNER_00000014 != SRC.PARTNER_ER));
To improve the speed, if you want to keep using SQL script, firstly you should replace the where statements with a join statement like below:
SELECT COUNT(*) FROM (
SELECT SRC.CLIENT,SRC.GUID
,COALESCE(SRC.PARTNER_ZSCHEDUL,'') PARTNER_ZSCHEDUL
,COALESCE(SRC.PARTNER_ZSVCUNIT,'') PARTNER_ZSVCUNIT
,COALESCE(SRC.PARTNER_ZPARTAPP,'') PARTNER_ZPARTAPP
,COALESCE(SRC.PARTNER_ZESCOWN,'') PARTNER_ZESCOWN
,COALESCE(SRC.PARTNER_ZESCAPRV,'') PARTNER_ZESCAPRV
,COALESCE(SRC.PARTNER_ZESCRQST,'') PARTNER_ZESCRQST
,COALESCE(SRC.PARTNER_ZPARTORD,'') PARTNER_ZPARTORD
,COALESCE(SRC.PARTNER_ER,'') PARTNER_ER
FROM KCP810_HBP.ZCRMD_ORDERADM_I_EXTN TGT
INNER JOIN (
SELECT CLIENT,GUID
, MIN(CASE WHEN PARTNER_FCT = 'ZSCHEDUL' THEN PARTNER END) PARTNER_ZSCHEDUL
, MIN(CASE WHEN PARTNER_FCT = 'ZSVCUNIT' THEN MERGED_PARTNER END) PARTNER_ZSVCUNIT
, MIN(CASE WHEN PARTNER_FCT = 'ZSVCUNIT' THEN PARTNER END) PARTNER_ZSVCUNIT_HR
, MIN(CASE WHEN PARTNER_FCT = 'ZPARTAPP' THEN PARTNER END) PARTNER_ZPARTAPP
, MIN(CASE WHEN PARTNER_FCT = 'ZESCOWN' THEN PARTNER END) PARTNER_ZESCOWN
, MIN(CASE WHEN PARTNER_FCT = 'ZESCAPRV' THEN PARTNER END) PARTNER_ZESCAPRV
, MIN(CASE WHEN PARTNER_FCT = 'ZESCRQST' THEN PARTNER END) PARTNER_ZESCRQST
, MIN(CASE WHEN PARTNER_FCT = 'ZPARTORD' THEN PARTNER END) PARTNER_ZPARTORD
, MIN(CASE WHEN PARTNER_FCT = '00000014' THEN PARTNER END) PARTNER_ER
FROM "_SYS_BIC"."CRM-SV.A-COMMON-VIEWS.Proc.Item/CV_IT_01_PARTNER_ALL" GROUP BY CLIENT,GUID ) SRC
ON TGT.CLIENT = SRC.CLIENT
AND TGT.GUID = SRC.GUID
AND ( TGT.PARTNER_ZSCHEDUL != SRC.PARTNER_ZSCHEDUL
OR TGT.PARTNER_ZSVCUNIT != SRC.PARTNER_ZSVCUNIT
OR TGT.PARTNER_ZPARTAPP != SRC.PARTNER_ZPARTAPP
OR TGT.PARTNER_ZESCOWN != SRC.PARTNER_ZESCOWN
OR TGT.PARTNER_ZESCAPRV != SRC.PARTNER_ZESCAPRV
OR TGT.PARTNER_ZESCRQST != SRC.PARTNER_ZESCRQST
OR TGT.PARTNER_ZPARTORD != SRC.PARTNER_ZPARTORD
OR TGT.PARTNER_00000014 != SRC.PARTNER_ER));
If this doesn't help you, you should use HANA's Analytic Views for which you could read here.

Converting SQL Query in to Linq or in lambda

I am trying to convert it in LINQ but I am not getting success , I would appreciate if someone help me out to convert it in linq or write the query using lambda expression
SELECT MAX('Quality Of Service') AS Category,
COUNT(CASE WHEN t.QualityOfService = 'Excellent' THEN 1 END) AS Excellent,
COUNT(CASE WHEN t.QualityOfService = 'VeryGood' THEN 1 END) AS Very_Good,
COUNT(CASE WHEN t.QualityOfService = 'Good' THEN 1 END) AS Good,
COUNT(CASE WHEN t.QualityOfService = 'Bad' THEN 1 END) AS Bad,
COUNT(CASE WHEN t.QualityOfService = 'Poor' THEN 1 END) AS Poor
FROM Feedbacks t
WHERE t.DateOfVisit BETWEEN '2018-03-29' AND '2018-03-29'
UNION
SELECT MAX('Quality Of Food') AS Category,
COUNT(CASE WHEN t.QualityOfFood = 'Excellent' THEN 1 END) AS Excellent,
COUNT(CASE WHEN t.QualityOfFood = 'VeryGood' THEN 1 END) AS Very_Good,
COUNT(CASE WHEN t.QualityOfFood = 'Good' THEN 1 END) AS Good,
COUNT(CASE WHEN t.QualityOfFood = 'Bad' THEN 1 END) AS Bad,
COUNT(CASE WHEN t.QualityOfFood = 'Poor' THEN 1 END) AS Poor
FROM Feedbacks t
WHERE t.DateOfVisit BETWEEN '2018-03-29' AND '2018-03-29'
UNION
SELECT MAX('Cleanliness Of Lounge') AS Category,
COUNT(CASE WHEN t.CleanlinessOfLounge = 'Excellent' THEN 1 END) AS Excellent,
COUNT(CASE WHEN t.CleanlinessOfLounge = 'VeryGood' THEN 1 END) AS Very_Good,
COUNT(CASE WHEN t.CleanlinessOfLounge = 'Good' THEN 1 END) AS Good,
COUNT(CASE WHEN t.CleanlinessOfLounge = 'Bad' THEN 1 END) AS Bad,
COUNT(CASE WHEN t.CleanlinessOfLounge = 'Poor' THEN 1 END) AS Poor
FROM Feedbacks t
WHERE t.DateOfVisit BETWEEN '2018-03-29' AND '2018-03-29'
UNION
SELECT MAX('Friendliness Of Staff') AS Category,
COUNT(CASE WHEN t.FriendlinessOfStaff = 'Excellent' THEN 1 END) AS Excellent,
COUNT(CASE WHEN t.FriendlinessOfStaff = 'VeryGood' THEN 1 END) AS Very_Good,
COUNT(CASE WHEN t.FriendlinessOfStaff = 'Good' THEN 1 END) AS Good,
COUNT(CASE WHEN t.FriendlinessOfStaff = 'Bad' THEN 1 END) AS Bad,
COUNT(CASE WHEN t.FriendlinessOfStaff = 'Poor' THEN 1 END) AS Poor
FROM Feedbacks t
WHERE t.DateOfVisit BETWEEN '2018-03-29' AND '2018-03-29'
UNION
SELECT MAX('Overall Experience') AS Category,
COUNT(CASE WHEN t.OverAllExperience = 'Excellent' THEN 1 END) AS Excellent,
COUNT(CASE WHEN t.OverAllExperience = 'VeryGood' THEN 1 END) AS Very_Good,
COUNT(CASE WHEN t.OverAllExperience = 'Good' THEN 1 END) AS Good,
COUNT(CASE WHEN t.OverAllExperience = 'Bad' THEN 1 END) AS Bad,
COUNT(CASE WHEN t.OverAllExperience = 'Poor' THEN 1 END) AS Poor
FROM Feedbacks t
WHERE t.DateOfVisit BETWEEN '2018-03-29' AND '2018-03-29'
The result of this query is coming in this way
Category Excellent Very_Good Good Bad Poor
Null 0 0 0 0 0
I have zero feedbacks right now in my table that is why it is showing null in category and all zero's.
My table looks like this
ID QualityOfFood QualityOfServices CleanlinessOfLounge FreindlinessOfStaff OverALLExperience
I asked this type of question but "this type" not the same , so requesting to don't mark it dublicate
I've Found it by using linqer
( from t in
( from t in db.Feedbacks
where
t.DateOfVisit >= "2018-03-29" && t.DateOfVisit <= "2018-03-29"
select new {
Column1 = "Quality Of Service",
Column2 =
t.QualityOfService == "Excellent" ? (System.Int64?)1 : null,
Column3 =
t.QualityOfService == "VeryGood" ? (System.Int64?)1 : null,
Column4 =
t.QualityOfService == "Good" ? (System.Int64?)1 : null,
Column5 =
t.QualityOfService == "Bad" ? (System.Int64?)1 : null,
Column6 =
t.QualityOfService == "Poor" ? (System.Int64?)1 : null,
Dummy = "x"
})
group t by new { t.Dummy } into g
select new {
Category = g.Max(p => "Quality Of Service"),
Excellent = g.Count(p => p.Column2 != null),
Very_Good = g.Count(p => p.Column3 != null),
Good = g.Count(p => p.Column4 != null),
Bad = g.Count(p => p.Column5 != null),
Poor = g.Count(p => p.Column6 != null)
}
).Union
( from t in
( from t in db.Feedbacks
where
t.DateOfVisit >= "2018-03-29" && t.DateOfVisit <= "2018-03-29"
select new {
Column1 = "Quality Of Food",
Column2 =
t.QualityOfFood == "Excellent" ? (System.Int64?)1 : null,
Column3 =
t.QualityOfFood == "VeryGood" ? (System.Int64?)1 : null,
Column4 =
t.QualityOfFood == "Good" ? (System.Int64?)1 : null,
Column5 =
t.QualityOfFood == "Bad" ? (System.Int64?)1 : null,
Column6 =
t.QualityOfFood == "Poor" ? (System.Int64?)1 : null,
Dummy = "x"
})
group t by new { t.Dummy } into g
select new {
Category = g.Max(p => "Quality Of Food"),
Excellent = g.Count(p => p.Column2 != null),
Very_Good = g.Count(p => p.Column3 != null),
Good = g.Count(p => p.Column4 != null),
Bad = g.Count(p => p.Column5 != null),
Poor = g.Count(p => p.Column6 != null)
}
).Union
( from t in
( from t in db.Feedbacks
where
t.DateOfVisit >= "2018-03-29" && t.DateOfVisit <= "2018-03-29"
select new {
Column1 = "Cleanliness Of Lounge",
Column2 =
t.CleanlinessOfLounge == "Excellent" ? (System.Int64?)1 : null,
Column3 =
t.CleanlinessOfLounge == "VeryGood" ? (System.Int64?)1 : null,
Column4 =
t.CleanlinessOfLounge == "Good" ? (System.Int64?)1 : null,
Column5 =
t.CleanlinessOfLounge == "Bad" ? (System.Int64?)1 : null,
Column6 =
t.CleanlinessOfLounge == "Poor" ? (System.Int64?)1 : null,
Dummy = "x"
})
group t by new { t.Dummy } into g
select new {
Category = g.Max(p => "Cleanliness Of Lounge"),
Excellent = g.Count(p => p.Column2 != null),
Very_Good = g.Count(p => p.Column3 != null),
Good = g.Count(p => p.Column4 != null),
Bad = g.Count(p => p.Column5 != null),
Poor = g.Count(p => p.Column6 != null)
}
).Union
( from t in
( from t in db.Feedbacks
where
t.DateOfVisit >= "2018-03-29" && t.DateOfVisit <= "2018-03-29"
select new {
Column1 = "Friendliness Of Staff",
Column2 =
t.FriendlinessOfStaff == "Excellent" ? (System.Int64?)1 : null,
Column3 =
t.FriendlinessOfStaff == "VeryGood" ? (System.Int64?)1 : null,
Column4 =
t.FriendlinessOfStaff == "Good" ? (System.Int64?)1 : null,
Column5 =
t.FriendlinessOfStaff == "Bad" ? (System.Int64?)1 : null,
Column6 =
t.FriendlinessOfStaff == "Poor" ? (System.Int64?)1 : null,
Dummy = "x"
})
group t by new { t.Dummy } into g
select new {
Category = g.Max(p => "Friendliness Of Staff"),
Excellent = g.Count(p => p.Column2 != null),
Very_Good = g.Count(p => p.Column3 != null),
Good = g.Count(p => p.Column4 != null),
Bad = g.Count(p => p.Column5 != null),
Poor = g.Count(p => p.Column6 != null)
}
).Union
( from t in
( from t in db.Feedbacks
where
t.DateOfVisit >= "2018-03-29" && t.DateOfVisit <= "2018-03-29"
select new {
Column1 = "Overall Experience",
Column2 =
t.OverAllExperience == "Excellent" ? (System.Int64?)1 : null,
Column3 =
t.OverAllExperience == "VeryGood" ? (System.Int64?)1 : null,
Column4 =
t.OverAllExperience == "Good" ? (System.Int64?)1 : null,
Column5 =
t.OverAllExperience == "Bad" ? (System.Int64?)1 : null,
Column6 =
t.OverAllExperience == "Poor" ? (System.Int64?)1 : null,
Dummy = "x"
})
group t by new { t.Dummy } into g
select new {
Category = g.Max(p => "Overall Experience"),
Excellent = g.Count(p => p.Column2 != null),
Very_Good = g.Count(p => p.Column3 != null),
Good = g.Count(p => p.Column4 != null),
Bad = g.Count(p => p.Column5 != null),
Poor = g.Count(p => p.Column6 != null)
}
)

Group By with 'HAVING' clause on slick+play

Imagine I have a SQL table grades which has amongst other fields, the name of the student and the result of the grade:
| student | grade |
|----------|:---------:|
| Harry | Good |
| Ron | Good |
| Harry | Average |
| Harry | Fail |
| Hermione | Excellent |
| Hermione | Excellent |
| Ron | Average |
| ..... | .... |
If I wanted to select all the students with at least two 'Excellent' and zero 'Fail' grades one could do:
select student
from grades
group by student
having
sum(case when grade = 'Excellent' then 1 else 0 end) >= 2 and
sum(case when grade = 'Fail' then 1 else 0 end)
How could I translate such a query into Slick?
On the documentation the 'Having' clause they give seems simpler.
gradesTables
.groupBy(._student)
.map{ case(student, group) => (student, ???)}
.filter(???)
.list
On a related note, why do I get an error with the following:
gradesTables
.groupBy(._student)
.map{ case(student, group) => (student, group.filter(_.grade == "Fail").length)}
.list
The error is:
slick.SlickTreeException: Cannot convert node to SQL Comprehension
The following code in Slick will generate the SQL you need:
val query: Query[(Rep[String], Rep[Option[Int]], Rep[Option[Int]]), (String, Option[Int], Option[Int]), Seq] =
grades.groupBy( _.student ).map{ case (student, group) =>
val groupList = group.map(_.grade)
val gradeExcel = groupList.map( grade =>
Case.If(grade === "Excellent").Then(1).Else(0) ).sum
val gradeFail = groupList.map( grade =>
Case.If(grade === "Fail").Then(1).Else(0) ).sum
(student, gradeExcel, gradeFail)
}.
filter( g => g._2 >= 2 && g._3 === 0 )
// ...
println("Generated SQL:\n" + query.result.statements)
// Generated SQL:
// List(
// select "STUDENT", sum((case when ("GRADE" = 'Excellent') then 1 else 0 end)),
// sum((case when ("GRADE" = 'Fail') then 1 else 0 end)) from "GRADES" group by "STUDENT"
// having (sum((case when ("GRADE" = 'Excellent') then 1 else 0 end)) >= 2) and
// (sum((case when ("GRADE" = 'Fail') then 1 else 0 end)) = 0)
// )
db.run(query.result.map(println))
// Vector((Hermione,Some(2),Some(0)))

Outer join operator not allowed with OR in my WHERE clause - Error: ORA-01719

I need to check for the conditions at the bottom, but I cannot change use OR because of the (+) that are involved. I did not set that part up, so I am not sure how to rearrange it. Can someone tell me what I could do to get those conditions to go through?
SELECT DISTINCT
t.tkt_sid,
ts.tkt_sts_dsc,
tp.prty_dsc,
a.agt_cont_nbr,
au_a.user_nm assigned_to,
t.rcd_lst_user_ts
FROM
afp_asd.tkt t,
afp_asd.tkt_prty tp,
afp_asd.tkt_sts ts,
afp_asd.tkt_type tt,
afp_asd.tkt_log tl,
afp_asd.agt a,
afp_asd.asd_user au_a,
afp_asd.asd_user au_c,
afp_asd.asd_user au_l,
afp_asd.asd_user au_log,
afp_asd.prb_area pa1,
afp_asd.prb_area pa2,
afp_asd.prb_area pa3,
afp_asd.mktg_org mo,
afp_asd.src_sys ss,
afp_asd.agt ofc,
afp_asd.co c,
afp_asd.agt_sts ast
WHERE (
t.prty_cd = tp.prty_cd
AND t.tkt_sts_cd = ts.tkt_sts_cd
AND t.tkt_type_cd = tt.tkt_type_cd
AND t.agt_sid = a.agt_sid
AND t.assigned_id = au_a.asd_user_id
AND t.rcd_crt_user_id = au_c.asd_user_id (+)
AND t.lst_updater_id = au_l.asd_user_id
AND t.tkt_sid = tl.tkt_sid
AND t.prb_area_sid = pa3.prb_area_sid
AND tl.rcd_crt_user_id = au_log.asd_user_id
AND pa3.prb_hier_sid = pa2.prb_area_sid (+)
AND pa2.prb_hier_sid = pa1.prb_area_sid (+)
AND a.mktg_org_cd = mo.mktg_org_cd
AND a.src_sys_cd = mo.src_sys_cd
AND a.src_sys_cd = ss.src_sys_cd
AND a.ofc_id = ofc.agt_cont_nbr (+)
AND a.src_sys_cd = ofc.src_sys_cd (+)
AND a.co_sid = c.co_sid (+)
AND a.agt_sts_cd = ast.agt_sts_cd
AND tl.rcd_lst_user_ts >= :b_start_date
AND t.user_grp_sid = :b_group_id
)
AND (
(TKT_STS_DSC NOT LIKE 'Completed')
OR (
tp.prty_dsc = 'High'
AND ts.tkt_sts_dsc = 'Pending'
AND t.rcd_lst_user_ts < SYSDATE- 2
)
OR (
t.rcd_lst_user_ts < SYSDATE- 3
AND ts.tkt_sts_dsc = 'In Progress'
AND tp.prty_dsc = 'High'
)
OR (
t.rcd_lst_user_ts < SYSDATE- 15
AND ts.tkt_sts_dsc = 'In Progress'
AND tp.prty_dsc = 'Medium'
)
OR (
t.rcd_lst_user_ts < SYSDATE- 28
AND ts.tkt_sts_dsc = 'In Progress'
AND tp.prty_dsc = 'Low'
)
OR (
t.rcd_lst_user_ts < SYSDATE- 7
AND ts.tkt_sts_dsc = 'Pending'
AND tp.prty_dsc = 'High'
)
OR (
t.rcd_lst_user_ts < SYSDATE- 21
AND ts.tkt_sts_dsc = 'Pending'
AND tp.prty_dsc = 'Medium'
)
OR (
t.rcd_lst_user_ts < SYSDATE- 43
AND ts.tkt_sts_dsc = 'Pending'
AND tp.prty_dsc = 'Low'
)
)
ORDER BY ASSIGNED_TO,
PRTY_DSC
Don't use the old FROM TableA,TableB WHERE ... join syntax. Just don't.
Instead, write out your joins individually:
FROM TableA
INNER JOIN TableB ON ...
LEFT JOIN TableC ON ...
This isn't just a general rant against the old syntax: using the new (I say "new", but it's more than 20 years old now), standard syntax will fix your problem in this case.

Progress DB, need to merge two queries

I have 2 progress database queries and I'm trying to merge them into one statement, but I am getting errors. Each of these queries simply returns a number and I would like to sum those 2 numbers together. Either that or make another query from scratch. They both take in a set of value codes for "DM1" and they both accept 1 "product".
Query 1
SELECT SUM(opn3.samt)
FROM PUB.ord ord3, PUB.opn opn3
WHERE
ord3.subsnum = opn3.subsnum
AND ord3.onum = opn3.onum
AND ord3.DM1 != ''
AND ord3.DM1 IN('XCWAJC25','WCWAMO73')
AND ord3.prdcde = 'CSC'
AND ord3.stat != 16
AND opn3.samt >= 0
GROUP BY ord3.DM1, ord3.prdcde
Query 2
SELECT SUM((-1 * opn2.samt) + ord2.samt)
FROM PUB.ord ord2, PUB.opn opn2
WHERE
ord2.subsnum = opn2.subsnum
AND ord2.onum = opn2.onum
AND ord2.DM1 != ''
AND ord2.DM1 IN('XCWAJC25','WCWAMO73')
AND ord2.prdcde = 'CSC'
AND ord2.stat = 16
AND opn2.samt < 0
GROUP BY ord2.DM1, ord2.prdcde
Merge attempt so far...
SELECT SUM(opn3.samt + (SELECT SUM((-1 * opn2.samt) + ord2.samt)
FROM PUB.ord ord2, PUB.opn opn2
WHERE
ord2.subsnum = opn2.subsnum
AND ord2.onum = opn2.onum
AND ord2.DM1 != ''
AND ord2.DM1 = ord3.DM1
AND ord2.prdcde = ord3.prdcde
AND ord2.stat = 16
AND opn2.samt < 0
GROUP BY ord2.DM1, ord2.prdcde
)) as foo
FROM PUB.ord ord3, PUB.opn opn3
WHERE
ord3.subsnum = opn3.subsnum
AND ord3.onum = opn3.onum
AND ord3.DM1 != ''
AND ord3.DM1 IN('XCWAJC25','WCWAMO73')
AND ord3.prdcde = 'CSC'
AND ord3.stat != 16
AND opn3.samt >= 0
GROUP BY ord3.DM1, ord3.prdcde
Thanks
I think this will work, although it would be nice to have sample data to verify:
SELECT COALESCE(SUM(a.samt), 0) - COALESCE(SUM(b.samt), 0)
+ COALESCE(SUM(CASE WHEN ord.stat = 16
AND b.samt < 0
THEN ord.samt END), 0)
FROM PUB.ord ord
LEFT JOIN PUB.opn a
ON a.subsnum = ord.subsnum
AND a.onum = ord.onum
AND a.samt >= 0
AND ord.stat != 16
LEFT JOIN PUB.opn b
ON b.subsnum = ord.subsnum
AND b.onum = ord.onum
AND b.samt < 0
AND ord.stat = 16
WHERE ord.DM1 IN('XCWAJC25', 'WCWAMO73')
AND ord.prdcde = 'CSC'
GROUP BY ord.DM1
Notes on query/stuff:
Always explicitly qualify joins, don't use the comma-separated FROM clause
I don't think you needed ord.DM1 != '', given that values have to be in a specific set
Putting a clause into a LEFT JOIN condition instead of the WHERE clause has a slightly different effect; it adds the condition to the join, instead of the filtering. This means that rows can be excluded based on something in the left table, regardless of whether you need the actual row (this is why ord.stat ended up in the LEFT JOINs). INNER JOINs would technically behave the same way, but it isn't usually noticeable because causing the right table to be excluded also excludes the left table.
I think this should do the trick, given the the individual queries work as intended:
SELECT sum1.tot + sum2.tot
FROM
(SELECT SUM(opn3.samt) as tot
FROM PUB.ord ord3, PUB.opn opn3
WHERE
ord3.subsnum = opn3.subsnum
AND ord3.onum = opn3.onum
AND ord3.DM1 != ''
AND ord3.DM1 IN('XCWAJC25','WCWAMO73')
AND ord3.prdcde = 'CSC'
AND ord3.stat != 16
AND opn3.samt >= 0
GROUP BY ord3.DM1, ord3.prdcde) sum1,
(SELECT SUM((-1 * opn2.samt) + ord2.samt) as tot
FROM PUB.ord ord2, PUB.opn opn2
WHERE
ord2.subsnum = opn2.subsnum
AND ord2.onum = opn2.onum
AND ord2.DM1 != ''
AND ord2.DM1 IN('XCWAJC25','WCWAMO73')
AND ord2.prdcde = 'CSC'
AND ord2.stat = 16
AND opn2.samt < 0
GROUP BY ord2.DM1, ord2.prdcde) sum2