SQL Query - Mathematical Query across 2 tables - sql

I think the Title is a bit off what I need, so if someone thinks of a better title please don't hesitate to change it.
My issue is this: I have a TIMESHEET table, and a RATES table.
The TIMESHEET table looks like this:
|RATE-ID|OT |DAY1|DAY2|DAY3|DAY4|DAY5|DAY6|DAY7|DAY8|DAY9|DAY10|DAY11|DAY12|DAY13|DAY14|
----------------------------------------------------------------------------------------
| 1 |1 | 2 | 3 | 4 | 5 | 6 | 0 | 0 | 7 | 8 | 1 | 2 | 0 | 5 | 6 |
| 5 |1.5| 6 | 5 | 4 | 3 | 2 | 0 | 0 | 1 | 0 | 7 | 6 | 4 | 3 | 2 |
The RATES table looks like this:
|ID| RATE |
|1 | 50.00 |
|2 | 30.00 |
....
|5 | 100.00|
So then what I do in terms of a query (once off, so no worries about SQL injections) is this:
SELECT sum(DAY1)+sum(DAY2)+sum(DAY3)+sum(DAY4)+sum(DAY5)+sum(DAY6)+sum(DAY7)+sum(DAY8)+sum(DAY9)+sum(DAY10)+sum(DAY11)+sum(DAY12)+sum(DAY13)+sum(DAY14) as TOTALHOURS, OT, RATES.RATE from TIMESHEET INNER JOIN RATES on TIMESHEETS.RATE-ID = RATES.ID
Works fantastic - 3 seperate columns that tell me the total hours, the OT multiplier, and the rate. Now comes the part I'm struggling with. I can't seem to multiply the TOTALHOURS column against anything. I can do the simple OT * RATE as NEWRATE and get it down to 2 columns: TOTALHOURS and NEWRATE.
My question is, how do I multiply these two dynamically named columns to get the result?
i.e the first one would be 49 (sum of line 1) * 1 (OT for line 1) * 50 (Rate for line 1) = 2450
Any help is greatly appreciated.
Cheers,
Dee.

If you want a single line query without the hassles of running subqueries, temp tables, CTE's, group by clauses, or what have you, just try cleaning up the query a bit like this:
SELECT sum((DAY1+DAY2+DAY3+DAY4+DAY5+DAY6+DAY7+DAY8+DAY9+DAY10+DAY11+DAY12+DAY13+DAY14) * RATE * OT) as TOTALL FROM from TIMESHEET INNER JOIN RATES on TIMESHEETS.RATE-ID = RATES.ID
SQL has a wide variety of ways to accomplish the same result. Try the different ones and find the type that suits you and the application well.

Related

Select Value based on Multiple Value Range in SQL

I am having multiple criteria to give incentive to my employees. For example as shown in below image
Grid Table is dynamic in nature. It keeps on changing based on business conditions.
I have a table where I have emp Ids whose Resolution % I have calculated and also calculated their Normalization %. Now, I need to give them % Incentives based on the above Grid using SQL Query.
Output Table in which i need to update the incentives
I assume the grid table is also stored as a database table (so you can update it):
+-----------------+---------------+--------------------+------------------+-----------+
| INCENTIVES |
+-----------------+---------------+--------------------+------------------+-----------+
| from_resulution | to_resolution | from_normalization | to_normalization | incentive |
+-----------------+---------------+--------------------+------------------+-----------+
| 0 | 70 | 0 | 5 | 9 |
| 0 | 70 | 5 | 10 | 11 |
| 0 | 70 | 10 | 100 | 13 |
| 71 | 75 | 0 | 5 | 10 |
... I hope you get the idea
+-----------------+---------------+--------------------+------------------+-----------+
And the update query can be:
update employee E
set E.incentive = (select I.incentive
from incentives I
where e.resolution >= I.from_resolution
and e.resolution < I.to_resolution
and e.normalization >= I.from_normalization
and e.normalization < I.to_normalization)
UPDATE: the TO values are not in the scope of the range. By using the TO value equal to the FROM value of the next range we assure to cover all values (including floating point). Thanks to Gordon

SQL Query to return tier based values

I have a database with a table for tier based pricing depending on the quantity bought example: (1-10) is $5, (11-15) is $10, 16 is $15, and 17-20 is $20
The table is structured in this way:
number int,
cost int
an example of the table:
number | cost
1 | 1
2 | 1
3 | 1
4 | 2
5 | 2
6 | 2
7 | 3
8 | 4
9 | 7
10 |7
Is there any way for me to write a query so that i can get these numbers returned in the format min, max, and cost for example running the query on the example above would return:
min|max|cost
-----|-----|----
1 | 10 | 5
11 |15 | 10
16 |16 | 15
17 |20 | 20
Also I am not sure if this is the best structure for such a table. Any and all help is appreciated. Thanks!
Try this. Its rather messy. Just tried it using my Server Management Studio.
Update: For better readability.
SELECT Mininum.Min, Maximum.Max, Mininum.Cost
FROM
(
SELECT MIN([Number]) as 'Min', Cost
FROM [TestDB].[dbo].[Testing]
GROUP BY Cost
) as [Mininum]
INNER JOIN
(
SELECT MAX([Number]) as 'Max', Cost
FROM [TestDB].[dbo].[Testing]
GROUP BY Cost
) as [Maximum]
ON Mininum.Cost = Maximum.Cost

1 to Many Query: Help Filtering Results

Problem: SQL Query that looks at the values in the "Many" relationship, and doesn't return values from the "1" relationship.
Tables Example: (this shows two different tables).
+---------------+----------------------------+-------+
| Unique Number | <-- Table 1 -- Table 2 --> | Roles |
+---------------+----------------------------+-------+
| 1 | | A |
| 2 | | B |
| 3 | | C |
| 4 | | D |
| 5 | | |
| 6 | | |
| 7 | | |
| 8 | | |
| 9 | | |
| 10 | | |
+---------------+----------------------------+-------+
When I run my query, I get multiple, unique numbers that show all of the roles associated to each number like so.
+---------------+-------+
| Unique Number | Roles |
+---------------+-------+
| 1 | C |
| 1 | D |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 4 | C |
| 4 | A |
| 5 | B |
| 5 | C |
| 5 | D |
| 6 | D |
| 6 | A |
+---------------+-------+
I would like to be able to run my query and be able to say, "When the role of A is present, don't even show me the unique numbers that have the role of A".
Maybe if SQL could look at the roles and say, WHEN role A comes up, grab unique number and remove it from column 1.
Based on what I would "like" to happen (I put that in quotations as this might not even be possible) the following is what I would expect my query to return.
+---------------+-------+
| Unique Number | Roles |
+---------------+-------+
| 1 | C |
| 1 | D |
| 5 | B |
| 5 | C |
| 5 | D |
+---------------+-------+
UPDATE:
Query Example: I am querying 8 tables, but I condensed it to 4 for simplicity.
SELECT
c.UniqueNumber,
cp.pType,
p.pRole,
a.aRole
FROM c
JOIN cp ON cp.uniqueVal = c.uniqueVal
JOIN p ON p.uniqueVal = cp.uniqueVal
LEFT OUTER JOIN a.uniqueVal = p.uniqueVal
WHERE
--I do some basic filtering to get to the relevant clients data but nothing more than that.
ORDER BY
c.uniqueNumber
Table sizes: these tables can have anywhere from 50,000 rows to 500,000+
Pretending the table name is t and the column names are alpha and numb:
SELECT t.numb, t.alpha
FROM t
LEFT JOIN t AS s ON t.numb = s.numb
AND s.alpha = 'A'
WHERE s.numb IS NULL;
You can also do a subselect:
SELECT numb, alpha
FROM t
WHERE numb NOT IN (SELECT numb FROM t WHERE alpha = 'A');
Or one of the following if the subselect is materializing more than once (pick the one that is faster, ie, the one with the smaller subtable size):
SELECT t.numb, t.alpha
FROM t
JOIN (SELECT numb FROM t GROUP BY numb HAVING SUM(alpha = 'A') = 0) AS s USING (numb);
SELECT t.numb, t.alpha
FROM t
LEFT JOIN (SELECT numb FROM t GROUP BY numb HAVING SUM(alpha = 'A') > 0) AS s USING (numb)
WHERE s.numb IS NULL;
But the first one is probably faster and better[1]. Any of these methods can be folded into a larger query with multiple additional tables being joined in.
[1] Straight joins tend to be easier to read and faster to execute than queries involving subselects and the common exceptions are exceptionally rare for self-referential joins as they require a large mismatch in the size of the tables. You might hit those exceptions though, if the number of rows that reference the 'A' alpha value is exceptionally small and it is indexed properly.
There are many ways to do it, and the trade-offs depend on factors such as the size of the tables involved and what indexes are available. On general principles, my first instinct is to avoid a correlated subquery such as another, now-deleted answer proposed, but if the relationship table is small then it probably doesn't matter.
This version instead uses an uncorrelated subquery in the where clause, in conjunction with the not in operator:
select num, role
from one_to_many
where num not in (select otm2.num from one_to_many otm2 where otm2.role = 'A')
That form might be particularly effective if there are many rows in one_to_many, but only a small proportion have role A. Of course you can add an order by clause if the order in which result rows are returned is important.
There are also alternatives involving joining inline views or CTEs, and some of those might have advantages under particular circumstances.

Find average for records with aggregated count

I am trying to find the average in a table that includes a count in each record.
I need to find the average as though there were individual records for each count listed in the record.
For example:
+-------+------------------+-------------------+
| Color | Value_to_Average | Number_of_Records |
+-------+------------------+-------------------+
| Red | 3 | 2 |
| Red | 2 | 3 |
| Green | 5 | 2 |
| Blue | 1 | 2 |
+-------+------------------+-------------------+
When I average the values individually, the result is 2.66667. How can I get this same result from the records with the counts?
SQL Fiddle
You want a weighted average:
select sum(Value_to_Average * Number_of_Records) / sum(Number_of_Records)
from Color_Avg t;
I think you're looking for something like this:
select (sum(value_to_average)*sum(number_of_records))/cast(sum(number_of_records) as double)
from table

Randomly Populating Foreign Key In Sample Data Set

I'm generating test data for a new database, and I'm having trouble populating one of the foreign key fields. I need to create a relatively large number (1000) of entries in a table (SurveyResponses) that has a foreign key to a table with only 6 entries (Surveys)
The database already has a Schools table that has a few thousand records. For arguments sake lets say it looks like this
Schools
+----+-------------+
| Id | School Name |
+----+-------------+
| 1 | PS 1 |
| 2 | PS 2 |
| 3 | PS 3 |
| 4 | PS 4 |
| 5 | PS 5 |
+----+-------------+
I'm creating a new Survey table. It will only have about 3 rows.
Survey
+----+-------------+
| Id | Col2 |
+----+-------------+
| 1 | 2014 Survey |
| 2 | 2015 Survey |
| 3 | 2016 Survey |
+----+-------------+
SurveyResponses simply ties a school to a survey.
Survey Responses
+----+----------+----------+
| Id | SchoolId | SurveyId |
+----+----------+----------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 1 |
| 4 | 4 | 3 |
| 5 | 5 | 2 |
+----+----------+----------+
Populating the SurveyId field is what's giving me the most trouble. I can randomly select 1000 Schools, but I haven't figured out a way to generate 1000 random SurveyIds. I've been trying to avoid a while loop, but maybe that's the only option?
I've been using Red Gate SQL Data Generator to generate some of my test data, but in this case I'd really like to understand how this can be done with raw SQL.
Here is one way, using a correlated subquery to get a random survey associated with each school:
select s.schoolid,
(select top 1 surveyid
from surveys
order by newid()
) as surveyid
from schools s;
Note: This doesn't seem to work. Here is a SQL Fiddle showing the non-workingness. I am quite surprised it doesn't work, because newid() should be a
EDIT:
If you know the survey ids have no gaps and start with 1, you can do:
select 1 + abs(checksum(newid()) % 3) as surveyid
I did check that this does work.
EDIT II:
This appears to be overly aggressive optimization (in my opinion). Correlating the query appears to fix the problem. So, something like this should work:
select s.schoolid,
(select top 1 surveyid
from surveys s2
where s2.surveyid = s.schoolid or s2.surveyid <> s.schoolid -- nonsensical condition to prevent over optimization
order by newid()
) as surveyid
from schools s;
Here is a SQL Fiddle demonstrating this.