T-sql :get SUM of Columns - sql

I have a table that looks something like the following :
W1 W2 w3
Gold 10 2 3
Silver 3 1 1
but i need a result :
W1 W2 w3
Gold 10 12 15
Silver 3 4 5
Is there any way i can get that result?
My sql query :
SELECT
week1=[1],week2=[2],week3=[3]
FROM
(
SELECT
[week]=DATEPART(ISO_WEEK,ta.enddate),ta.id
FROM
table1 ta where ta.enddate BETWEEN '2016/01/01' AND '2016/12/31'
) src
PIVOT
(
SUM(id) FOR week IN (
[1],[2],[3])
) piv

Does this do what you want?
select t.??, t.w1, (t.w1 + t.w2) as w2, (t.w1 + t.w2 + t.w3) as w3
from table1 t;
I don't know what the name of the first column is, so I just used ??.

One thought, since you tagged this question with Reporting Services. If, in the end, you are displaying the info using Reporting Services I would highly consider using the Matrix tool to do the pivoting and summation of the data because that is exactly what it does.
To further explain as it seems you are going to use SSRS. Your matrix would have a dataset that would be similar to this:
SELECT
[week]=DATEPART(ISO_WEEK,ta.enddate),
ta.id,
ta.MetalType as GoldorSilver
FROM table1 ta
where ta.enddate BETWEEN '2016/01/01' AND '2016/12/31'
The matrix would have a header and footer and column group would be [Week] with a Column Group total to do the sum across the week. The row group footer would do the sum across all weeks.

Calculate the running total before pivoting the data
SELECT element,
week1=[1],week2=[2],week3=[3]
FROM
(
SELECT [week] = DATEPART(ISO_WEEK,ta.enddate),
price = sum(ta.price)Over(Partition by element Order by enddate),
element
FROM table1 ta
where ta.enddate BETWEEN '2016/01/01' AND '2016/12/31'
) src
PIVOT
(
SUM(price) FOR week IN ( [1],[2],[3])
) piv
for older versions
SELECT element,
week1=[1],week2=[2],week3=[3]
FROM
(
SELECT [week] = DATEPART(ISO_WEEK,ta.enddate),
cs.price,
element
FROM table1 ta
cross apply(select sum(price) from table1 tb
where ta.element = tb.element and ta.enddate >= tb.enddate ) cs (price)
where ta.enddate BETWEEN '2016/01/01' AND '2016/12/31'
) src
PIVOT
(
SUM(price) FOR week IN ( [1],[2],[3])
) piv

Related

MS-ACCESS / SQL - How to apply where clause in multiple conditions

SELECT Stock.*
FROM Stock
WHERE (
(
(Stock.ComputerPartNumber) In (SELECT [ComputerPartNumber] FROM [Stock] As Tmp GROUP BY [ComputerPartNumber] HAVING Count(*)=2)
)
AND
(
(Stock.EquipmentName)="EquipmentA" Or (Stock.EquipmentName)="EquipmentB")
)
OR (
(
(Stock.ComputerPartNumber) In (SELECT [ComputerPartNumber] FROM [Stock] As Tmp GROUP BY [ComputerPartNumber] HAVING Count(*)=1)
)
AND (
(Stock.EquipmentName)="EquipmentA" Or (Stock.EquipmentName)="EquipmentB"
)
);
I am using the above SQL to achieve below 3 items:-
Find out all of the ComputerPartNumber which used by EquipmentA and/or EquipmentB only
Filter out the query result if the ComputerPartNumber used by equipment other than EquipmentA and EquipmentB.
If the ComputerPartNumber is used by both EquipmentA and EquipmentC, filter out the result also.
However the item 3 cannot be filtered out successfully. What should I do in order to achieve the item3?
Table and Query snapshots are attached. Thanks in advance!
Table
Query
What you need to do is to check if the total number of times a part is used in all pieces of Equipment is equal to the total number of times a part is used by either Equipment A or B:
SELECT S.StorageID, S.ComputerPartNumber, S.EquipmentName, S.Result
FROM Stock AS S
WHERE
(SELECT COUNT(*) FROM Stock AS S1 WHERE S1.ComputerPartNumber=S.ComputerPartNumber)
=(SELECT COUNT(*) FROM Stock AS S2 WHERE S2.ComputerPartNumber=S.ComputerPartNumber AND S2.EquipmentName IN("EquipmentA","EquipmentB"))
Regards,
You can use not exists:
select s.*
from stock as s
where not exists (select 1
from stock as s2
where s2.ComputerPartNumber = s.ComputerPartNumber and
s2.EquipmentName not in ("EquipmentA", "EquipmentB")
);

Find duplicates in MS SQL table

I know that this question has been asked several times but I still cannot figure out why my query is returning values which are not duplicates. I want my query to return only the records which have identical value in the column Credit. The query executes without any errors but values which are not duplicated are also being returned. This is my query:
Select
_bvGLTransactionsFull.AccountDesc,
_bvGLAccountsFinancial.Description,
_bvGLTransactionsFull.TxDate,
_bvGLTransactionsFull.Description,
_bvGLTransactionsFull.Credit,
_bvGLTransactionsFull.Reference,
_bvGLTransactionsFull.UserName
From
_bvGLAccountsFinancial Inner Join
_bvGLTransactionsFull On _bvGLAccountsFinancial.AccountLink =
_bvGLTransactionsFull.AccountLink
Where
_bvGLTransactionsFull.Credit
IN
(SELECT Credit AS NumOccurrences
FROM _bvGLTransactionsFull
GROUP BY Credit
HAVING (COUNT(Credit) > 1 ) )
Group By
_bvGLTransactionsFull.AccountDesc, _bvGLAccountsFinancial.Description,
_bvGLTransactionsFull.TxDate, _bvGLTransactionsFull.Description,
_bvGLTransactionsFull.Credit, _bvGLTransactionsFull.Reference,
_bvGLTransactionsFull.UserName, _bvGLAccountsFinancial.Master_Sub_Account,
IsNumeric(_bvGLTransactionsFull.Reference), _bvGLTransactionsFull.TrCode
Having
_bvGLTransactionsFull.TxDate > 01 / 11 / 2014 And
_bvGLTransactionsFull.Reference Like '5_____' And
_bvGLTransactionsFull.Credit > 0.01 And
_bvGLAccountsFinancial.Master_Sub_Account = '90210'
That's because you're matching on the credit field back to your table, which contains duplicates. You need to isolate the rows that are duplicated with ROW_NUMBER:
;WITH CTE AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY CREDIT ORDER BY (SELECT NULL)) AS RN
FROM _bvGLTransactionsFull)
Select
CTE.AccountDesc,
_bvGLAccountsFinancial.Description,
CTE.TxDate,
CTE.Description,
CTE.Credit,
CTE.Reference,
CTE.UserName
From
_bvGLAccountsFinancial Inner Join
CTE On _bvGLAccountsFinancial.AccountLink = CTE.AccountLink
WHERE CTE.RN > 1
Group By
CTE.AccountDesc, _bvGLAccountsFinancial.Description,
CTE.TxDate, CTE.Description,
CTE.Credit, CTE.Reference,
CTE.UserName, _bvGLAccountsFinancial.Master_Sub_Account,
IsNumeric(CTE.Reference), CTE.TrCode
Having
CTE.TxDate > 01 / 11 / 2014 And
CTE.Reference Like '5_____' And
CTE.Credit > 0.01 And
_bvGLAccountsFinancial.Master_Sub_Account = '90210'
Just as a side note, I would consider using aliases to shorten your queries and make them more readable. Prefixing the table name before each column in a join is very difficult to read.
I trust your code in terms of extracting all data per your criteria. With this, let me have a different approach and see your script "as-is". So then, lets keep first all the records in a temp.
Select
_bvGLTransactionsFull.AccountDesc,
_bvGLAccountsFinancial.Description,
_bvGLTransactionsFull.TxDate,
_bvGLTransactionsFull.Description,
_bvGLTransactionsFull.Credit,
_bvGLTransactionsFull.Reference,
_bvGLTransactionsFull.UserName
-- temp table
INTO #tmpTable
From
_bvGLAccountsFinancial Inner Join
_bvGLTransactionsFull On _bvGLAccountsFinancial.AccountLink =
_bvGLTransactionsFull.AccountLink
Where
_bvGLTransactionsFull.Credit
IN
(SELECT Credit AS NumOccurrences
FROM _bvGLTransactionsFull
GROUP BY Credit
HAVING (COUNT(Credit) > 1 ) )
Group By
_bvGLTransactionsFull.AccountDesc, _bvGLAccountsFinancial.Description,
_bvGLTransactionsFull.TxDate, _bvGLTransactionsFull.Description,
_bvGLTransactionsFull.Credit, _bvGLTransactionsFull.Reference,
_bvGLTransactionsFull.UserName, _bvGLAccountsFinancial.Master_Sub_Account,
IsNumeric(_bvGLTransactionsFull.Reference), _bvGLTransactionsFull.TrCode
Having
_bvGLTransactionsFull.TxDate > 01 / 11 / 2014 And
_bvGLTransactionsFull.Reference Like '5_____' And
_bvGLTransactionsFull.Credit > 0.01 And
_bvGLAccountsFinancial.Master_Sub_Account = '90210'
Then remove the "single occurrence" data by creating a row index and remove all those 1 time indexes.
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY Credit ORDER BY Credit) AS rowIdx
, *
FROM #tmpTable) AS innerTmp
WHERE
rowIdx != 1
You can change your preference through PARTITION BY <column name>.
Should you have any concerns, please raise it first as these are so far how I understood your case.
EDIT : To include those credits that has duplicates.
SELECT
tmp1.*
FROM #tmpTable tmp1
RIGHT JOIN (
SELECT
Credit
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY Credit ORDER BY Credit) AS rowIdx
, *
FROM #tmpTable) AS innerTmp
WHERE
rowIdx != 1
) AS tmp2
ON tmp1.Credit = tmp2.Credit

How do I get the top 10 results of a query?

I have a postgresql query like this:
with r as (
select
1 as reason_type_id,
rarreason as reason_id,
count(*) over() count_all
from
workorderlines
where
rarreason != 0
and finalinsdate >= '2012-12-01'
)
select
r.reason_id,
rt.desc,
count(r.reason_id) as num,
round((count(r.reason_id)::float / (select count(*) as total from r) * 100.0)::numeric, 2) as pct
from r
left outer join
rtreasons as rt
on
r.reason_id = rt.rtreason
and r.reason_type_id = rt.rtreasontype
group by
r.reason_id,
rt.desc
order by r.reason_id asc
This returns a table of results with 4 columns: the reason id, the description associated with that reason id, the number of entries having that reason id, and the percent of the total that number represents.
This table looks like this:
What I would like to do is only display the top 10 results based off the total number of entries having a reason id. However, whatever is leftover, I would like to compile into another row with a description called "Other". How would I do this?
with r2 as (
...everything before the select list...
dense_rank() over(order by pct) cause_rank
...the rest of your query...
)
select * from r2 where cause_rank < 11
union
select
NULL as reason_id,
'Other' as desc,
sum(r2.num) over() as num,
sum(r2.pct) over() as pct,
11 as cause_rank
from r2
where cause_rank >= 11
As said above Limit and for the skipping and getting the rest use offset... Try This Site
Not sure about Postgre but SELECT TOP 10... should do the trick if you sort correctly
However about the second part: You might use a Right Join for this. Join the TOP 10 Result with the whole table data and use only the records not appearing on the left side. If you calculate the sum of those you should get your "Sum of the rest" result.
I assume that vw_my_top_10 is the view showing you the top 10 records. vw_all_records shows all records (including the top 10).
Like this:
SELECT SUM(a_field)
FROM vw_my_top_10
RIGHT JOIN vw_all_records
ON (vw_my_top_10.Key = vw_all_records.Key)
WHERE vw_my_top_10.Key IS NULL

SQL query ...multiple max value selection. Help needed

Business World 1256987 monthly 10 2009-10-28
Business World 1256987 monthly 10 2009-09-23
Business World 1256987 monthly 10 2009-08-18
Linux 4 U 456734 monthly 25 2009-12-24
Linux 4 U 456734 monthly 25 2009-11-11
Linux 4 U 456734 monthly 25 2009-10-28
I get this result with the query:
SELECT DISTINCT ljm.journelname,ljm. subscription_id,
ljm.frequency,ljm.publisher, ljm.price, ljd.receipt_date
FROM lib_journals_master ljm,
lib_subscriptionhistory
lsh,lib_journal_details ljd
WHERE ljd.journal_id=ljm.id
ORDER BY ljm.publisher
What I need is the latest date in each journal?
I tried this query:
SELECT DISTINCT ljm.journelname, ljm.subscription_id,
ljm.frequency, ljm.publisher, ljm.price,ljd.receipt_date
FROM lib_journals_master ljm,
lib_subscriptionhistory lsh,
lib_journal_details ljd
WHERE ljd.journal_id=ljm.id
AND ljd.receipt_date = (
SELECT max(ljd.receipt_date)
from lib_journal_details ljd)
But it gives me the maximum from the entire column. My needed result will have two dates (maximum of each magazine), but this query gives me only one?
You could change the WHERE statement to look up the last date for each journal:
AND ljd.receipt_date = (
SELECT max(subljd.receipt_date)
from lib_journal_details subljd
where subljd.journelname = ljd.journelname)
Make sure to give the table in the subquery a different alias from the table in the main query.
You should use Group By if you need the Max from date.
Should look something like this:
SELECT
ljm.journelname
, ljm.subscription_id
, ljm.frequency
, ljm.publisher
, ljm.price
, **MAX(ljd.receipt_date)**
FROM
lib_journals_master ljm
, lib_subscriptionhistory lsh
, lib_journal_details ljd
WHERE
ljd.journal_id=ljm.id
GROUP BY
ljm.journelname
, ljm.subscription_id
, ljm.frequency
, ljm.publisher
, ljm.price
Something like this should work for you.
SELECT ljm.journelname
, ljm.subscription_id
, ljm.frequency
, ljm.publisher
, ljm.price
,md.max_receipt_date
FROM lib_journals_master ljm
, ( SELECT journal_id
, max(receipt_date) as max_receipt_date
FROM lib_journal_details
GROUP BY journal_id) md
WHERE ljm.id = md.journal_id
/
Note that I have removed the tables from the FROM clause which don't contribute anything to the query. You may need to replace them if yopu simplified your scenario for our benefit.
Separate this into two queries one will get journal name and latest date
declare table #table (journalName as varchar,saleDate as datetime)
insert into #table
select journalName,max(saleDate) from JournalTable group by journalName
select all fields you need from your table and join #table with them. join on journalName.
Sounds like top of group. You can use a CTE in SQL Server:
;WITH journeldata AS
(
SELECT
ljm.journelname
,ljm.subscription_id
,ljm.frequency
,ljm.publisher
,ljm.price
,ljd.receipt_date
,ROW_NUMBER() OVER (PARTITION BY ljm.journelname ORDER BY ljd.receipt_date DESC) AS RowNumber
FROM
lib_journals_master ljm
,lib_subscriptionhistory lsh
,lib_journal_details ljd
WHERE
ljd.journal_id=ljm.id
AND ljm.subscription_id = ljm.subscription_id
)
SELECT
journelname
,subscription_id
,frequency
,publisher
,price
,receipt_date
FROM journeldata
WHERE RowNumber = 1

Math with previous row in SQL, avoiding nested queries?

I want to do some math on the previous rows in an SQL request in order to avoid doing it in my code.
I have a table representing the sales of two entities (the data represented here is doesn't make much sense and it's just an excerpt) :
YEAR ID SALES PURCHASE MARGIN
2009 1 10796820,57 2662369,19 8134451,38
2009 2 2472271,53 2066312,34 405959,19
2008 1 9641213,19 1223606,68 8417606,51
2008 2 3436363,86 2730035,19 706328,67
I want to know how the sales, purchase, margin... have evolved and compare one year to the previous one.
In short I want an SQL result with the evolutions pre-computed like this :
YEAR ID SALES SALES_EVOLUTION PURCHASE PURCHASE_EVOLUTION MARGIN MARGIN_EVOLUTION
2009 1 10796820,57 11,99 2662369,19 117,58 8134451,38 -3,36
2009 2 2472271,53 -28,06 2066312,34 -24,31 405959,19 -42,53
2008 1 9641213,19 1223606,68 8417606,51
2008 2 3436363,86 2730035,19 706328,67
I could do some ugly stuff :
SELECT *, YEAR, ID, SALES , (SALES/(SELECT SALES FROM TABLE WHERE YEAR = OUTER_TABLE.YEAR-1 AND ID = OUTER_TABLE.ID) -1)*100 as SALES_EVOLUTION (...)
FROM TABLE as OUTER_TABLE
ORDER BY YEAR DESC, ID ASC
But I have arround 20 fields for which I would have to do a nested query, meaning I would have a very huge and ugly query.
Is there a better way to do this, with less SQL ?
Using sql server (but this should work for almost any sql), with the table provided you can use a LEFT JOIN
DECLARE #Table TABLE(
[YEAR] INT,
ID INT,
SALES FLOAT,
PURCHASE FLOAT,
MARGIN FLOAT
)
INSERT INTO #Table ([YEAR],ID,SALES,PURCHASE,MARGIN) SELECT 2009,1,10796820.57,2662369.19,8134451.38
INSERT INTO #Table ([YEAR],ID,SALES,PURCHASE,MARGIN) SELECT 2009,2,2472271.53,2066312.34,405959.19
INSERT INTO #Table ([YEAR],ID,SALES,PURCHASE,MARGIN) SELECT 2008,1,9641213.19,1223606.68,8417606.51
INSERT INTO #Table ([YEAR],ID,SALES,PURCHASE,MARGIN) SELECT 2008,2,3436363.86,2730035.19,706328.67
SELECT cur.*,
((cur.Sales / prev.SALES) - 1) * 100
FROM #Table cur LEFT JOIN
#Table prev ON cur.ID = prev.ID AND cur.[YEAR] - 1 = prev.[YEAR]
The LEFT JOIN will allow you to still see values from 2008, where an INNER JOIN would not.
Old skool solution:
SELECT c.YEAR, c.ID, c.SALES, c.PURCHASE, c.MARGIN
, p.YEAR, p.ID, p.SALES, p.PURCHASE, p.MARGIN
FROM tab AS c -- current
INNER JOIN tab AS p -- previous
ON c.year = p.year - 1
AND c.id = p.id
If you have a db with analytical functions (MS SQL, Oracle) you can use the LEAD or LAG analytical functions, see http://www.oracle-base.com/articles/misc/LagLeadAnalyticFunctions.php
I think this would be the correct application:
SELECT c.YEAR, c.ID, c.SALES, c.PURCHASE, c.MARGIN
, LAG(c.YEAR, 1, 0) OVER (ORDER BY ID,YEAR)
, LAG(c.ID, 1, 0) OVER (ORDER BY ID,YEAR)
, LAG(c.SALES, 1, 0) OVER (ORDER BY ID,YEAR)
, LAG(c.PURCHASE, 1, 0) OVER (ORDER BY ID,YEAR)
, LAG(c.MARGIN, 1, 0) OVER (ORDER BY ID,YEAR)
FROM tab AS c -- current
(not really sure, haven't played with this enough)
You can do it like this:
SELECT t1.*, t1.YEAR, t1.ID, t1.SALES , ((t1.sales/t2.sales) -1) * 100 as SALES_EVOLUTION
(...)
FROM Table t1 JOIN Table t2 ON t1.Year = (t2.Year + 1) AND t1.Id = t2.Id
ORDER BY t1.YEAR DESC, t1.ID ASC
Now, if you want to compare more years, you'd have to do more joins, so it is a slightly ugly solution.