SQL query unique values based on 2 colums - sql-server-2008

I need help figuring this out.
At the moment I have this query:
SELECT Table1.Column1 AS Company
,SUM(DATEDIFF(Day, Table2.Column2, Table1.Column3)) AS DiffDate
FROM Table1
INNER JOIN Table2 ON Table1.Column6 = Table2.Column6
AND Table1.Column5 = Table2.Column5
GROUP BY Company
The result of this is...
Company DiffDate
company1 8
But this is not what I want because there are values that are the combinations of column5 and column6 that are the same. See below.
Company | Column6 | Column5 | DiffDate
Company1 | 5782 | 10 | 2
Company1 | 5782 | 10 | 2
Company1 | 5782 | 20 | 2
Company1 | 5782 | 30 | 2
So the result I'm after is 6, not 8.
I tried using SELECT DISTINCT but that doesn't do anything.
Thank you in advance.
edit 2016-02-09
I've made the following query in SSMS.
;WITH cte AS (SELECT DISTINCT bestlevdat, bestradnr, bestnr FROM dbo.bpa)
SELECT dbo.bp.ftgnr AS Företagsnr,SUM(DATEDIFF(Day,cte.bestlevdat, dbo.bp.bestberlevdat)) AS Diff_Bekräftat_dat,
SUM(DATEDIFF(Day,cte.bestlevdat, dbo.bp.bestbeglevdat)) AS Diff_Önskat_dat, COUNT(dbo.bp.bestradnr) AS AntalRader, SUM(CASE WHEN datediff(day, cte.bestlevdat, dbo.bp.bestberlevdat) < - 0 THEN 1 ELSE 0 END) AS Antal_avvikande_rader, ROUND((COUNT(dbo.bp.bestradnr) - SUM(CASE WHEN datediff(day, cte.bestlevdat, dbo.bp.bestberlevdat) < - 0 THEN 1.0 ELSE 0.0 END)) * 100 / COUNT(dbo.bp.bestradnr), 1) AS Levsäk
FROM dbo.bp INNER JOIN
cte ON dbo.bp.bestnr = cte.bestnr AND dbo.bp.bestradnr = cte.bestradnr
WHERE (YEAR(dbo.bp.bestberlevdat) = '2015')
GROUP BY dbo.bp.ftgnr
ORDER BY AntalRader DESC
This works a charm. But when I create a new view and paste this query there and try to save I get an error on the ";" again. If I remove ";" in SSMS I get error "The multi-part identifier "cte.bestlevdat" could not be bound" and also converts my query to the following:
WITH cte AS (SELECT DISTINCT bestlevdat, bestradnr, bestnr
FROM dbo.bpa AS bpa_1)
SELECT TOP (100) PERCENT dbo.bp.ftgnr AS Företagsnr, SUM(DATEDIFF(Day, cte_1.bestlevdat, dbo.bp.bestberlevdat)) AS Diff_Bekräftat_dat, SUM(DATEDIFF(Day,
cte_1.bestlevdat, dbo.bp.bestbeglevdat)) AS Diff_Önskat_dat, COUNT(dbo.bp.bestradnr) AS AntalRader, SUM(CASE WHEN datediff(day, cte.bestlevdat,
dbo.bp.bestberlevdat) < - 0 THEN 1 ELSE 0 END) AS Antal_avvikande_rader, ROUND((COUNT(dbo.bp.bestradnr) - SUM(CASE WHEN datediff(day, cte.bestlevdat,
dbo.bp.bestberlevdat) < - 0 THEN 1.0 ELSE 0.0 END)) * 100 / COUNT(dbo.bp.bestradnr), 1) AS Levsäk
FROM dbo.bp INNER JOIN
cte AS cte_1 ON dbo.bp.bestnr = cte_1.bestnr AND dbo.bp.bestradnr = cte_1.bestradnr
WHERE (YEAR(dbo.bp.bestberlevdat) = '2015')
GROUP BY dbo.bp.ftgnr
ORDER BY AntalRader DESC
Sorry if I've missed something obvious, but I'm pretty new with querys and this is my first attempt on "View".

Sounds like you want to do something like you want the distinct rows from Table2, and to join/aggregate them from Table1, so using a CTE may help, so something like:
;WITH cte AS (
SELECT DISTINCT
Column2,
Column5,
Column6
FROM Table2
)
SELECT
Table1.Column1 AS Company,
SUM(DATEDIFF(Day, cte.Column2, Table1.Column3)) AS DiffDate
FROM Table1
INNER JOIN cte
ON Table1.Column6 = cte.Column6
AND Table1.Column5 = cte.Column5
GROUP BY Company

Related

SQL to display rows greater than or equal to a row value by keeping the same id and loop for all id's

I have 3 columns production number(int) , op number(int) and value(float). No column is distinct by itself. I need to look for the values <= 0 and display everything that's within that production number(int)
Example :
PO# | OP# | values
5247 | 100 | 12.0
5247 | 200 | 22.0
5247 | 300 | -12.0
5247 | 400 | 52.0
6328 | 100 | 11.0
6328 | 300 | 55.0
I need to get these two rows
5247, 300 , -12.0 and
5247, 400 , 52.0
not any other rows. How do I do that?
Just another guess to improve #DarshanMehta query:
http://sqlfiddle.com/#!9/0a3567/2
SELECT t.*
FROM prodOps t
INNER JOIN
(SELECT po, op
FROM prodOps
WHERE `values` < 0) f
ON t.OP >= f.op AND f.po=t.po
but what will happen if you have several records with negative values?
Check this fiddle:
http://sqlfiddle.com/#!9/138f44/1
and guess-solution is:
SELECT t.*
FROM prodOps t
INNER JOIN
(SELECT po, MAX(op) op
FROM prodOps
WHERE `values` < 0
GROUP BY po) f
ON t.OP >= f.op AND f.po=t.po
But I would suggest you to rethink the problem and redesign table and refactor your app. All this way goes to nowhere.
using exists()
select *
from t
where exists (
select 1
from t as i
where t.PO = i.PO
and t.OP >= i.OP
and i.value < 0
)
If I understand your requiremetns
Select A.*
From YourTable A
Join (
Select [PO#]
,min([Values]) as minV
,max([Values]) as maxV
From YourTable
Group by [PO#]
Having min([Values])<0
) B
on A.[PO#]=B.[PO#] and A.[Values] in (B.minV,B.maxV)
Returns
PO# OP# values
5247 300 -12
5247 400 52
Can you try the below query:
SELECT *
FROM table t
WHERE t.OP >=
(SELECT MAX(OP)
FROM table
WHERE PO = t.PO AND value < 0);
update
Here's the SQL Fiddle.

Calculate percent increase/decrease from previous row value

I have a table that looks something like this:
|date_start | date_end |amount |
+------------+-------------+-------+
|2015-02-23 | 2015-03-01 |50 |
|2015-03-02 | 2015-03-08 |50 |
|2015-03-09 | 2015-03-15 |100 |
|2015-03-16 | 2015-03-22 |800 |
|2015-03-23 | 2015-03-29 |50 |
and I'd like to work out the percent increase/decrease for column amount, from the previous date. For example the result would be something like this,
|date_start | date_end |amount | perc_change |
+------------+-------------+-------+-------------+
|2015-02-23 | 2015-03-01 |50 |
|2015-03-02 | 2015-03-08 |50 | 0
|2015-03-09 | 2015-03-15 |100 | 50
|2015-03-16 | 2015-03-22 |800 | 700
|2015-03-23 | 2015-03-29 |50 | -750
I've searched and racked my brain for a couple of days now. Usually, I simply do this using server side code but now I need to contain it all within the query.
Try this:
SELECT t.*,
amount - (SELECT amount FROM transactions prev WHERE prev.date_end < t.date_start ORDER BY date_start DESC LIMIT 1) AS changes
FROM transactions t
If we assume that the previous row always ends exactly one day before the current begins (as in your sample data), then you can use a join. The percentage increase would be:
select t.*,
100 * (t.amount - tprev.amount) / tprev.amount
from atable t left join
atable tprev
on tprev.date_end = t.date_start - interval 1 day;
However, your results seem to just have the difference, which is easier to calculate:
select t.*,
(t.amount - tprev.amount) as diff
from atable t left join
atable tprev
on tprev.date_end = t.date_start - interval 1 day;
you can use window function it would be easier
select date_start,
date_end,
amount,
LAG(amount, 1, 0) OVER (ORDER BY date_start) as previous_amount,
amount - LAG(amount, 1, 0) OVER (ORDER BY date_start) as amount_diff,
((amount - LAG(amount, 1, 0) OVER (ORDER BY date_start)) / (amount - LAG(amount, 1, 0) OVER (ORDER BY date_start))) * 100 amount_diff_percentage
from your_table_name
I started by joining each row in the table with the one that comes after it, like this:
SELECT m.date_start AS mStart, mt.date_start AS mtStart
FROM myTable m
JOIN myTable mT ON m.date_start < mt.date_start AND mt.date_start = (SELECT MIN(date_start) FROM myTable WHERE date_start > m.date_start);
This will join the tables so that each row can be seen with the following date if there is one. If there's not, the date is not returned.
Once you have those values, you can adjust the SELECT query to show you the percent change from the date before, like this:
SELECT
mT.date_start AS secondDate,
mT.amount - m.amount AS percentChange
FROM myTable m
JOIN myTable mT ON m.date_start < mT.date_start AND mt.date_start = (SELECT MIN(date_start) FROM myTable WHERE date_start > m.date_start);
I would like to make a note here that while you say 'percent difference' in your question, your expected results have nothing to do with percentage, but just difference in value. If you need to calculate this a different way, you can just adjust the select query above to meet your needs.
The last thing you will have to do is join this back to your original table to see all of the values together. This has to be done using a left join, in order for the first date of the table to be seen. Here is the final query:
SELECT m.date_start, m.date_end, m.amount, tmp.percentChange
FROM myTable m
LEFT JOIN(
SELECT
mT.date_start AS secondDate,
mT.amount - m.amount AS percentChange
FROM myTable m
JOIN myTable mT ON m.date_start < mT.date_start AND mt.date_start = (SELECT MIN(date_start) FROM myTable WHERE date_start > m.date_start)
) tmp ON tmp.secondDate = m.date_start;
And here is an SQL Fiddle example.
It looks something like this, i dont have environment to test this select but i think it should work
SELECT t1.date_start, t1.date_end, t1.amount, (t1.amount - t2.amount) as perc_change
FROM table1 t1
JOIN table1 t2 ON t1.id = t2.id + 1 // here your join to
//get next row and calculate this perc_change field
You case use the mysql var system :
SELECT date_start, date_end, IF(#last IS NOT NULL,ammount - #last , '' ) as perc_change, #last := amount as amount
From table
ORDER BY date_start;
the var #last is set at each passage, so column order between perc_change and amount is important

Selecting totals as three fields from same table as one query?

I have a table with various orders in it:
ID | Date | etc...
1 | 2013-01-01 | etc
2 | 2013-02-01 | etc
3 | 2013-03-01 | etc
4 | 2013-04-01 | etc
5 | 2013-05-01 | etc
6 | 2013-06-01 | etc
7 | 2013-06-01 | etc
8 | 2013-03-01 | etc
9 | 2013-04-01 | etc
10 | 2013-05-01 | etc
I want a query that ends wit the result:
overallTotal | totalThisMonth | totalLastMonth
10 | 2 | 1
But I want to do this in one query! I am trying to find a way to use subqueries to do this. SO far I have:
SELECT * from (
SELECT count(*) as overallTotal from ORDERS
)
How can I combine this with other subqueries so I can get the totals in one query?
UPDATE
Original question was for MySQL, but I need it for Firebird now.
With conditional sums you can do it (MySQL syntax):
select
count(*) as overallTotal,
sum(if(Month(Date)+12*Year(Date)=Month(GetDate())+12*Year(GetDate()), 1, 0))
as totalThisMonth
sum(if(Month(Date)+12*Year(Date)=Month(GetDate())+12*Year(GetDate())-1, 1, 0))
as totalThisMonth;
from mytable
Use the month+12*year formula to avoid the problem with year change.
UPDATE
With Firebird the same applies, you only have to replace Month(x) with EXTRACT (MONTH FROM x), Year(x) with EXTRACT (YEAR FROM x) and Getdate() with CURRENT_TIME. This will look ugly, so I won't put it here, but you could easily do it yourself.
Posubly use SUM of the result of IF statements, with the IF checking that it is a relevant date.
SELECT COUNT(*),
SUM(IF(YEAR(Date) = YEAR(CURDATE()) AND MONTH(Date) = MONTH(CURDATE()), 1, 0))
SUM(IF((YEAR(Date) = YEAR(CURDATE()) AND MONTH(Date) = MONTH(CURDATE()) - 1) OR (YEAR(Date) = YEAR(CURDATE()) - 1 AND MONTH(Date) = 12 AND MONTH(CURDATE()) = 1), 1, 0))
from ORDERS
Complexity is caused by coping with a change of year when looking for the previous month
I see you are already using sub queries, so why not do something like the following,
SELECT
(SELECT count(*) from ORDERS) as overallTotal,
(SELECT count(*) from ORDERS where Date between ... and ...) as totalThisMonth,
(SELECT count(*) from ORDERS where Date between ... and ...) as overallTotal
SELECT
(SELECT COUNT(*) FROM C AS total) AS T,
(SELECT COUNT(*) AS thisMonth FROM C WHERE MONTH(d) = 6) AS A,
(SELECT COUNT(*) AS lastMonth FROM C WHERE MONTH(d) = 5) AS B;
Please notice "month" are "hard coded" -- but it shouldn't be too difficult to extract the "current month" at application level. If you really need to, you could do it at SQL level with something like that:
SELECT
(SELECT COUNT(*) FROM C AS total) AS T,
(SELECT COUNT(*) AS thisMonth FROM C WHERE MONTH(d) = MONTH(NOW()) ) AS A,
(SELECT COUNT(*) AS lastMonth FROM C WHERE MONTH(d) = (MONTH(NOW())+11)%12) ) AS B;

Subcount's as fields in a mysql query

For a statistics application, with a table structure as such:
unique_id | browser_family | os | date_time | js_enabled | flash_enabled | non_binary_field
1 firefox w7 ... 1 0 yes
2 chrome w7 ... 1 1 no
3 ie9 wx ... 0 0 yes
So, I'd like to perform a query with where clauses on any fields, and have it give me counts of js_enabled=1, flash_enabled =0, non_binary_field = 'yes' for those criteria (say `os` = 'w7' and date(`date_time`) = '01-08-2012').
The result would be:
count(js_enabled=1) | count(flash_enabled=1) | count(non_binary_field='yes')
2 1 1
Is this possible in a single query?
Thanks!
select sum(js_enabled=1),
sum(flash_enabled=1),
sum(non_binary_field='yes')
from your_table
where `os` = 'w7'
and date(`date_time`) = '2012-08-01'
Each field can be filled by a separate subquery:
select
(select count(js_enabled) from yourtable where js_enabled=1),
(select count(flash_enabled) from yourtable where flash_enabled=1),
(select count(non_binary_field) from yourtable where non_binary_field='yes')

MySQL SELECT to Rank A Number out of a set of numbers

I have rows of data from a SELECT query with a few prices (say three for this example). One is our price, one is competitor1 price, one is competitor2 price. I want to add a column that spits out the rank of our price as compared to the other two prices; if our price is the lowest it would spit out the number 1 if the highest it would spit out the number it is out of.
Something like this:
Make | Model | OurPrice | Comp1Price | Comp2Price | Rank | OutOf
MFG1 MODEL1 350 100 500 2 3
MFG1 MODEL2 50 100 100 1 3
MFG2 MODEL1 100 NULL 50 2 2
MFG2 MODEL2 9999 500 NULL 2 2
-Sometimes the competitor price will be NULL as seen above, and I believe this is where my issue lies. I have tried a CASE and it works when only on one competitor but when I add a AND statement it spits out the ranks as all NULL. Is there a better way of doing this through a MySQL query?
SELECT
MT.MAKE as Make,
MT.MODEL as Model,
MT.PRICE as OurPrice,
CT1.PRICE as Comp1Price,
CT2.PRICE as Comp2Price,
CASE
WHEN MT.PRICE < CT1.PRICE AND MT.PRICE < CT2.PRICE
THEN 1 END AS Rank
(CT1.PRICE IS NOT NULL) + (CT2.PRICE IS NOT NULL) + 1 as OutOf
FROM mytable MT
LEFT JOIN competitor1table as CT1 ON CT1.MODEL = MT.MODEL
LEFT JOIN competitor2table as CT2 ON CT2.MODEL = MT.MODEL
ORDER BY CLASS
Not tested, but you can try:
SELECT
a.MAKE AS Make,
a.MODEL AS Model,
a.PRICE AS OurPrice
MAX(CASE WHEN a.compnum = 1 THEN pricelist END) AS Comp1Price,
MAX(CASE WHEN a.compnum = 2 THEN pricelist END) AS Comp2Price,
FIND_IN_SET(a.PRICE, GROUP_CONCAT(a.pricelist ORDER BY a.pricelist)) AS Rank,
COUNT(a.pricelist) AS OutOf
FROM
(
SELECT MAKE, MODEL, PRICE, PRICE AS pricelist, 0 AS compnum
FROM mytable
UNION ALL
SELECT a.MAKE, a.MODEL, a.PRICE, CT1.PRICE, 1
FROM mytable a
LEFT JOIN competitor1table CT1 ON a.MODEL = CT1.MODEL
UNION ALL
SELECT a.MAKE, a.MODEL, a.PRICE, CT2.PRICE, 2
FROM mytable a
LEFT JOIN competitor2table CT2 ON a.MODEL = CT2.MODEL
) a
GROUP BY
a.MAKE, a.MODEL
(CT1.PRICE IS NOT NULL AND CT1.PRICE < MT.PRICE) + (CT2.PRICE IS NOT NULL AND CT2.PRICE < MT.PRICE) + 1 as Rank