SQL: Improve Select Query - c#

I have reasons to believe that this code can be done much better, and probably doing much more of the work with the query to begin with?
I'll try to explain this.
Each row on my database has either a 1, 2 or 3 value for exercise, and then also a number as rep that could be any number, but in this code I choose to only care for 1-12, so this code selects the row that has the highest value of kilograms (which is a column and has a value on each row) where exercise is 1 and rep is 1, and then 2 and 3 etc. etc. up to 12, then changes exercise to 2 and goes from 1-12 again selecting the top kilograms row.
Does this make sense?
for (var i = 1; i <= 3; i++) {
for (var ii = 1; ii <= 12; ii++) {
var getPR = "SELECT top 1 kg, rep, date FROM Test WHERE exerVariName = 'Comp' AND exercise = #0 AND rep = #1 order by kg desc";
db.Execute(getPR, i, ii);
foreach (var get in db.Query(getPR, i, ii)) {
DateTime Date = get.Date;
var finalDate = Date.ToString("MMM d, yyyy");
var weight = get.kg + "kg";
var reps = "x " + get.rep;
<a>#weight #reps - #finalDate</a>
<br>
}
}
}
I use SQL Server Compact, and it's not a MVC project.

You can select all rows you are interested in with only one query using Group By and MAX aggregated function.
SELECT t.kg, t.rep, t.date
FROM Test t
INNER JOIN
(SELECT MAX(kg) as kg, exercise, rep
FROM Test
WHERE exerVariName = 'Comp'
GROUP BY exercise, rep) i
ON t.exercise = i.exercise AND t.rep = i.rep AND t.kg = i.kg
WHERE t.exerVariName = 'Comp'
Inner query is executed only once. It finds a group identifier (exercise, rep) tuple and a corresponding maximum kg group value.
Then inner query is joined with Test table in order to get "content" of rows (in your case only one additional field date).
Overall performance is quit optimal.
You need only to iterate over results of this query.
See this topic.
Edit:
Exclude multiple (rep, exercise) records having same kg (almost same result as OP's looping)
SELECT kg, rep, exercise, MAX(date)
FROM
(SELECT t.kg, t.rep, t.exercise, t.date
FROM Test t
INNER JOIN
(SELECT MAX(kg) as kg, exercise, rep
FROM Test
WHERE exerVariName = 'Comp'
GROUP BY exercise, rep) i
ON t.exercise = i.exercise AND t.rep = i.rep AND t.kg = i.kg
WHERE t.exerVariName = 'Comp') t
GROUP BY t.kg, t.rep, t.exercise

SELECT kg, rep, date, exercise, rep FROM Test test1 WHERE rep = (SELECT TOP 1 test2.rep FROM Test test2 WHERE test2.exercise = test1.exercise AND test2.rep = test1.rep ORDER BY kg DESC) GROUP BY exercise, rep
Loop over those results and display them.

Related

How to make a schedule table query from SQL Server 2012?

I need help to make a schedule table for employee from my table the main problem is the row not start from first row on every column so the row keep continue, see picture below:
we can see that the next column is null on the first row and continue from the last row. this is my query and table relations, you can see it below.
SELECT (SELECT PEGAWAI.NAMAPEGAWAI
WHERE (JADWAL.HARIKERJA = 'Tuesday')) AS Selasa,
(SELECT PEGAWAI.NAMAPEGAWAI
WHERE (JADWAL.HARIKERJA = 'Wednesday')) AS Rabu,
(SELECT PEGAWAI.NAMAPEGAWAI
WHERE (JADWAL.HARIKERJA = 'Thursday')) AS Kamis,
(SELECT PEGAWAI.NAMAPEGAWAI
WHERE (JADWAL.HARIKERJA = 'Friday')) AS Jumat,
(SELECT PEGAWAI.NAMAPEGAWAI
WHERE (JADWAL.HARIKERJA = 'Sabtu')) AS Saturday,
(SELECT PEGAWAI.NAMAPEGAWAI
WHERE (JADWAL.HARIKERJA = 'Monday')) AS Minggu FROM JADWAL INNER JOIN
JADWALPEGAWAI ON JADWAL.IDJADWAL = JADWALPEGAWAI.IDJADWAL INNER JOIN
PEGAWAI ON JADWALPEGAWAI.IDPEGAWAI = PEGAWAI.IDPEGAWAI WHERE (JADWAL.SHIFT = 'I')
What seems to be causing your query not to behave as you expect, is that you have no GROUP BY clause in your query, which means that you get one row of output for each for (in this case) each row of the JadwalPegawai table.
At a minimum, adding GROUP BY Pegawai.NamaPegawai after your WHERE … clause should fix this, but I think that we can do even better:
Select Min(iif(j.Harikerja = 'Tuesday', p.NamaPegawai, Null)) As Selasa,
Min(iif(j.Harikerja = 'Wednesday', p.NamaPegawai, Null)) As Rabu,
…
From Jadwal As j
Join JadwalPegawai As jp On j.IdJadwal = jp.IdJadwal
Join Pegawai As p On jp.IdPegawai = p.IdPegawai
Where j.Shift = 'I'
Group By p.NamaPegawai;
This should be logically equivalent, but doesn't use subqueries. Hope I didn't make any spelling errors, I'm not too good at bahasa Indonesia...

SQL Server query : get the sum conditionally

Given data
I need to come up to this by including SalesNew column:
SalesNew column will compute for the sum of each items sales based on their group number conditionally.
Example based on the table above. when the group number of item is 1, IT WILL JUST COPY OR RETAIN ITS SALES. When it is not 1, SalesNew column should get the sum of all items per group, like in the given example, salesNew displays 20 for item 3 and item 4 because it adds their sales that both having 10. So item 3 and 4 salesNew value is 20.
I know the sum function but this doesn't display the desired output. I hope anyone could help me out on this.
Thanks in advance
You could use SUM(Sales) OVER(PARTITION BY [Group]), with a combination of CASE:
SELECT *,
salesNew =
CASE
WHEN [Group] = 1 THEN Sales
ELSE SUM(Sales) OVER(PARTITION BY [Group])
END
FROM Data
SQL Fiddle
You could also use CROSS APPLY:
SELECT
d.*,
salesNew =
CASE
WHEN [Group] = 1 THEN Sales
ELSE x.salesNew
END
FROM Data d
CROSS APPLY(
SELECT salesNew = SUM(Sales)
FROM Data
WHERE [Group] = d.[Group]
)x
SQL Fiddle

Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause

This may be a very silly mistake but I just can't fix it. I have 2 tables, Questions and Questions_Rating.
Questions:
question_id question user_id
------------------------------------------
1 'How to blablabla' 1
2 'bla bla bla' 1
Questions_Rating
In this table, users will rate questions either by +1 or -1
question_rating_id question_id user_id rate
------------------------------------------------------
1 1 2 (+1)
2 1 3 (+1)
3 1 4 ( 1)
Now I would simply like to fetch the question from the questions table, and the SUM of the rate in the questions_rating table for this question, which has an ID = 1.
String QUERY = "SELECT q.question, SUM(r.rate) FROM questions q, questions_rating r WHERE r.question_id = q.question_id AND q.question_id = 1";
And this is what I'm getting.
Column 'questions.question' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I know that the SUM of the rate will return 1 row, and the question is supposedly 1 row, I can't figure out what's wrong with this query.
I am using SQL Server 2008.
You will have to Group the values as per the question so you need a
GROUP BY q.question
at the end of your query
Good Explanation here
You need the group by q.question in your SQL statement.
Your Sum is suppose to return only a single row against the table, since you are selecting a column along with the Sum you need to specify a group on the selected column. In your case it should be q.question, and sum will be applied to each group of questions.
SELECT q.question,SUM(r.rate) AS RateSum
FROM questions q,questions_rating r
WHERE r.question_id = q.question_id AND q.question_id=1
GROUP BY q.question
or (using ANSI 92 style joins)
SELECT q.question,SUM(r.rate) AS RateSum
FROM questions q INNER JOIN questions_rating r ON r.question_id = q.question_id
WHERE q.question_id=1
GROUP BY q.question
or (using subqueries)
SELECT q.question, (SELECT SUM(r.rate) FROM questions_rating r WHERE r.question_id = q.question_id) AS RateSum
FROM questions q
WHERE q.question_id=1
You are missing group by clause in your select statement.
As you have used sum() function which is an aggregate function
So you need to write group by for all the select columns.
String QUERY = "SELECT
q.question,
SUM(r.rate)
FROM
questions q,
questions_rating r
where
r.question_id = q.question_id AND
q.question_id=1
group by
q.question";

LINQ GroupBy confusion

I have
var result = (from rev in Revisions
join usr in Users on rev.UserID equals usr.ID
join clc in ChangedLinesCounts on rev.Revision equals clc.Revision
select new {rev.Revision,
rev.Date, usr.UserName, usr.ID, clc.LinesCount}).Take(6);
I make a couple of joins on different tables, not relevant for this question what keys are, but at the end of this query my result "table" contains
{Revision, Date, UserName, ID, LinesCount}
Now I execute e GroupBy in order to calculate a total lines count per user.
So..
from row in result group row by row.ID into g {1}
select new {
g.Key,
totalCount = g.Sum(count=>count.LinesCount)
};
So I get a Key=ID, and totalCount=Sum, but
Confusion
I would like to have also other fields in final result.
In my understanding "table" after {1} grouping query consist of
{Revision, Date, UserName, ID, LinesCount, TotalCount}
If my assumption is correct, why I can not do something like this:
from row in result group row by row.ID into g {1}
select new {
g.Key,
g.Revision //Revision doesn't exist ! Why ??
totalCount = g.Sum(count=>count.LinesCount)
};
but
from row in result group row by row.ID into g {1}
select new {
g.Key,
Revision = g.Select(x=>x.Revision), //Works !
totalCount = g.Sum(count=>count.LinesCount)
};
Works !, but imo, sucks, cause I execute another Select.
Infact looking on LinqPad SQL output I get 2 SQL queries.
Question
Is there any elegant and optimal way to do this, or I always need to run Select
on groupped data, in order to be able to access the fields, that exists ?
The problem is, that you only group by ID - if you'd do that in SQL, you couldn't access the other fields either...
To have the other fields as well, you have to include them in you group clause:
from row in result group row by new { row.ID, row.Revision } into g
select new {
g.Key.ID,
g.Key.Revision
totalCount = g.Sum(count=>count.LinesCount)
};
The problem here is your output logically looks something like this:
Key = 1
Id = 1, Revision = 3587, UserName = Bob, LinesCount = 34, TotalCount = 45
Id = 1, Revision = 3588, UserName = Joe, LinesCount = 64, TotalCount = 54
Id = 1, Revision = 3589, UserName = Jim, LinesCount = 37, TotalCount = 26
Key = 2
Id = 2, Revision = 3587, UserName = Bob, LinesCount = 34, TotalCount = 45
Id = 2, Revision = 3588, UserName = Joe, LinesCount = 64, TotalCount = 54
Id = 2, Revision = 3589, UserName = Jim, LinesCount = 37, TotalCount = 26
Much like if you were to perform a an SQL GROUP BY, an value is either part of the key and thus unique per group, or is in the details and thus is repeated multiple times and possibly different for each row.
Now, logically, it might be that Revision and UserName are unique for each Id but Linq has no way to know that (the same as SQL has no way to know that).
To solve this you'll need to some how specify which revision you want. For instance:
Revision = g.FirstOrDefault(x => x.Revision)
To avoid the multiple SQL problem you would need to use an aggregate function that can be translated in to SQL since most SQL dialects do not have a first operator (the result set is considered unordered so technically no item is "first").
Revision = g.Min(x => x.Revision)
Revision = g.Max(x => x.Revision)
Unfortunately Linq does not have a min/max operator for strings, so although the SQL might support this, Linq does not.
In this case you can produce an intermediate result set for the Id and totals, then join this back to the original set to get the details, eg:
from d in items
join t in (
from t in items
group by t.Id into g
select new { Id = g.Key, Total = g.Sum(x => x.LineCount) }
) on d.Id equals t.Id
select new { Id = d.Id, Revision = d.Revision, Total = t.Total }
Revision doesn't exist in your second example because it's not a member of IGrouping<T>, in IGrouping<T> you have a Key property, and it's also an IEnumerable<T> for all the rows grouped together. Thus each of those rows has a Revision, but there is no Revision for the grouping itself.
If the Revision will be the same for all rows with the same ID, you could use FirstOrDefault() so that the select nets at most one answer:
from row in result group row by row.ID into g {1}
select new {
g.Key,
Revision = g.Select(x=>x.Revision).FirstOrDefault(),
totalCount = g.Sum(count=>count.LinesCount)
};
If the Revision is not unique per ID, though, you'd want to use an anonymous type as #Tobias suggests for the grouping, then you will get a grouping based on ID and Revision.

sql query to linq-to-entities

How can this query be transform to linq
SELECT materialId, SUM(totalAmount) as quantity FROM Inventory
It's the sum part that I don't know how...
query = from inv in context.Inventory
select new MaterialQuantity()
{
MaterialId = inv.materialId,
Quantity = ??
};
EDIT
Trying to sum the value of totalAmount.
It's a view that is
materialId totalSum and other fields
1 5
1 10
1 20
So I want my linq to return me
MaterialId = 1, Quantity = 35
I'm going to give a complete guess here... assuming your inventory has multiple rows with the same materialId and you want to sum in those groups, you could use:
var query = from inv in content.Inventory
group inv.totalAmount by inv.materialId into g
select new { MaterialId = g.Key, Quantity = g.Sum() };
If you're not trying to group though, you'll need to clarify your question. Sample data and expected output would help.

Categories

Resources