Same query fetching different results - sql-server

I am following this article series to learn about ROLLUP and CUBE
http://beyondrelational.com/modules/2/blogs/28/posts/10471/tsql-lab-6-using-with-cube-to-generate-subtotal-and-grand-total-rows.aspx
For this query:
select
case
when grouping(CustomerName) = 1 then 'All Customers'
else CustomerName
end as CustomerName,
case
when grouping(ItemName) = 1 then 'All Items'
else ItemName
end as ItemName,
sum(quantity*pricepercase) as Amount1
from orders
group by CustomerName, ItemName
with cube
The author has result like this:
CustomerName ItemName Amount
-------------------- -------------------- ---------------------
Jacob Item 1 312.50
Jacob Item 2 480.00
Jacob All Items 792.50
Mike Item 1 75.00
Mike Item 2 44.00
Mike All Items 119.00
All Customers All Items 911.50
All Customers Item 1 387.50
All Customers Item 2 524.00
The two extra rows generated by cube are the last 2 rows. I get results like this:
CustomerName ItemName Amount
-------------------- -------------------- ---------------------
Jacob Item 1 312.50
Mike Item 1 75.00
All Customers Item 1 387.50
Jacob Item 2 480.00
Mike Item 2 44.00
All Customers Item 2 524.00
All Customers All Items 911.50
Jacob All Items 792.50
Mike All Items 119.00
The first result set looks appropriate. Why is there a difference when I run it?

IIRC SQL does not guarantee any order except when you explicitely have an ORDER BY in place... sometimes different SQL Server versions/patchlevels "order" differently without an ORDER BY.
I don't know whether the author produced that result using SQL Server 2005 or 2008 or 2008 R2 etc. BUT I higghly suspect that this is the reason for what you see...
IF you need a specific order in the resultset always use an explicit ORDER BYclause!

Related

SQL delete rows based on date difference

The situation is quite complicated to express in the title. An example should be much easier to understand.
My table A:
uid id ticket created_date
001 1 movie 2015-01-23 08:23:16
002 25 TV 2012-01-13 12:02:20
003 1 movie 2015-02-01 07:15:36
004 1 movie 2014-02-15 15:38:40
What I need to achieve is to remove duplicate records that appear within 31 days between each other and retain the record that appear first. So the above table would be reduced to B:
uid id ticket created_date
001 1 movie 2015-01-23 08:23:16
002 25 TV 2012-01-13 12:02:20
004 1 movie 2014-02-15 15:38:40
because the 3rd row in A were within 31 days of row 1 and it appeared later than row 1 (2015-02-01 vs 2015-01-23), so it gets removed.
Is there a clean way to do this?
I would suggest the following approach:
SELECT A.uid AS uid
INTO #tempA
FROM A
LEFT JOIN A AS B
ON A.id=B.id AND A.ticket=B.ticket
WHERE DATEDIFF(SECOND,B.date,A.date) > 0 AND
DATEDIFF(SECOND,B.date,A.date) < 31*24*60*60;
DELETE FROM A WHERE uid IN (SELECT uid FROM #tempA);
This is assuming that by 'duplicate records' you mean records that have both identical id as well as identical ticket fields. If that's not the case you should adjust the ON clause accordingly.

Dynamic Sql Filter using tables

Any ideas on how this should be done with T-SQL queries?
I have two tables, Table A contain records I want to return but filter through. Table B contains the list of filters and class categories. New records are added to Table A all the time. The goal is to dynamically categorized records in Table A based on the filters listed in Table B.
Example:
Table A
Name
------------
John Doe
Mary Lamb
Peter Pan
Tom Sawyer
Suzie Lamb
Nancy Lamb
Josh Reddin
Table B:
Filter | Category
----------------------
John%Doe% | Team 1
%Lamb% | Team 2
Tom% | Team 1
Desired output:
Name | Category
John Doe | Team 1
Tom Sawyer | Team 1
Mary Lamb | Team 2
Suzie Lamb | Team 2
Nancy Lamb | Team 2
Peter Pan |
Josh Reddin |
I thought about doing the following but not sure if that's the best solution:
SELECT Filter, category from TableB (Get list of filters)
Using SQL Loop through filters returned in (1.) and find matches in Table A using LIKE.
Example:
SELECT name, Category
FROM Table A, Table B
WHERE Table A.Name Like (CURRENT filter FROM B)
Insert/append record(s) returned in (2.) into TempTable
SELECT *
FROM TempTable (this returns Names and categories as shown in the desired output)
UNION
SELECT *
FROM Table A
RIGHT OUTER JOIN TempTable on NAME
WHERE Category in null
(This returns rows with no categories found...Peter Pan and Josh Reddin)
Any ideas?
How about performance?
Thanks.
You can use combination of like and left join
select a.Name,b.Category
from tableA a left join tableB b on a.name like b.Filter

SQL Server aggregate function with sum

I want to use an aggregate function in SQL Server to sum the number of seats:
The table is like that (it's all the same software just version changes)
OrderID | CustomerID | ProductID | Product Name | No of Seats
1 | 11 | 351-0212-4 | soft v601,Download | 3
2 | 11 | 361-0313-5 | soft v701,Upgrade | 2
3 | 12 | 341-1210-4 | soft v501,Download | 5
4 | 12 | 351-0212-5 | soft v601,Upgrade | 2
...
And I want a result like
Sum(no of seats)
8
So If a customer already bought the software but have upgraded keep number of seats for the customer.
e.g.:
Customer 11 bought 3 licences of our soft and then he bought two upgrades of a newer vesion so the sum for him should be 3 instead of 5.
Is that something possible to do in SQL ?
I hope I've been clear if not let me know.
Thanks in advance.
something like
select CustomerID, sum([No of Seats])
from <your table>
where [Product Name] not like '%upgrade%'
group by CustomerID
But in general - filter out those you don't want to see in the results and then sum. And if you want total number (not per customer):
select sum([No of Seats])
from <your table>
where [Product Name] not like '%upgrade%'
you should add boolean column, like 'isActive', then your select can be like this
select customerid, sum(numberOfSeats) from table
where isActive = 1
group by customerid
You have some normalization problems. The Product Name column is (presumably) redundant with the ProductID column, plus Product Name
apparently carries two logically distinct pieces of information: the name itself, and whether that product is an upgrade.
It would be better to split this into two tables, say Products and Orders. The Products table would have columns ProductID (primary key), Product_Name, and Is_Upgrade, and the Orders table would have columns OrderID (primary key), CustomerID (foreign key), ProductID (foreign key), and NumberOfSeats.
Given what you now have, however, and assuming that you want to avoid counting seats where the product name ends in 'Upgrade', you seem to want a query along these lines:
SELECT SUM("No of seats")
FROM Orders
WHERE CustomerID = 11 AND "Product Name" NOT LIKE '%Upgrade'

sql pivot function for a table with only 2 columns

I'm trying to use the pivot function in SQL Server to transform some results, but I'm running into trouble.
The table only has 2 columns, which look like this:
company category
----- -----
company 1 Arcade
company 1 Action
company 2 Arcade
company 2 Adventure
I'd like to transform it to this:
company category 1 category 2
----- ----- -----
company 1 Arcade Action
company 2 Arcade Adventure
So far all I can find are examples of pivot functions where there is a 3rd column in the original results with "category 1" or "category 2", which then uses the values in those column as the names of the new, pivoted columns.
What I want to do is simply define the names of the columns from scratch. Is there a way to do this with the pivot function?
Thanks in advance!
Since you need a third column that contains category1, category2, etc, then I would suggest applying a windowing function like row_number() to your data first before attempting to convert the data into columns. The row_number() function will create a unique sequenced number for each company and category, you will then use this calculated value to pivot the data.
The easiest way to convert the data would be to use an aggregate function along with a CASE expression. First, you will use a subquery to generate the row_number():
select company,
max(case when seq = 1 then category end) Category1,
max(case when seq = 2 then category end) Category2
from
(
select company, category,
row_number() over(partition by company
order by company) seq
from yourtable
) d
group by company;
See SQL Fiddle with Demo.
Now, if you want to use the PIVOT function you would still use the row_number(), but you would place the new calculated sequence as the new column names:
select company, category1, category2
from
(
select company, category,
'category'+
cast(row_number() over(partition by company
order by company) as varchar(10)) seq
from yourtable
) d
pivot
(
max(category)
for seq in (Category1, Category2)
) piv;
See SQL Fiddle with Demo. These generate a result of:
| COMPANY | CATEGORY1 | CATEGORY2 |
|-----------|-----------|-----------|
| company 1 | Arcade | Action |
| company 2 | Arcade | Adventure |

Outputting Results from complicated database structure (SQL Server)

This will be a long question so I'll try and explain it as best as I can.
I've developed a simple reporting tool in which a number of results are stored and given a report id, these results were generated from a particular quote being used on the main system, with a huge list of these being stored in a quotes table. Here are the current batch:
REPORTS
REP_ID DESC QUOTE_ID
-----------------------------------
1 Test 1
2 Today 1
3 Last Week 2
RESULTS
RES_ID TITLE REFERENCE REP_ID
---------------------------------------------------
1 Equipment Toby 1
2 Inventory Carl 1
3 Stocks Guest 2
4 Portfolios Guest 3
QUOTE
QUOTE_ID QUOTE
------------------------------------
1 Booking a meeting room
2 Car Park Policy
3 New User Guide
So far, so good, a simple stored procedure was able to pull all the information necessary.
Now, the feature list has been upped to include categories and groups of the quotes. In the Reports table quote_id has been changed to group_id to link to the following tables.
REPORTS
- REPORT_ID
- DESC
- GROUP_ID
GROUP
- GROUP_ID
- GROUP
GROUP_CAT_JOIN
- GCJ_ID
- CAT_ID
- GROUP_ID
CATEGORIES
- CAT_ID
- CATEGORY
CAT_QUOTE_JOIN
- CQJ_ID
- CAT_ID
- QUOTE_ID
The idea of these changes is so that instead of running a report on a quote I should now write a report for a group where a group is a set of quotes for certain occasions. I should also be able to run a report on a category where a category is also a set of quotes for certain departments. The trick is that several categories can fall into one group.
To explain it further, the results table has a report_id that links to reports, reports has a group_id that links to groups, groups and categories are linked through a group_cat_join table, the same with categories and quotes through a cat_quote_join table.
In basic terms I should be able to pull all the results from either a group of quotes or a category of quotes. The query will aim to pull all the results from a certain report under either a certain category, a group or both. This puzzle has left me stumped for days now as inner joins don't appear to be working and I'm struggling to find other ways to solve the problem using SQL.
Can anyone here help me?
Here's some extra clarification.
I want to be able to return all the results within a category, but as of right now the solution below and the ones I've tried always output every solution within a description, which is not what I want.
Here's an example of the data I have in there at the moment
Results
RES_ID TITLE REFERENCE REP_ID
---------------------------------------------------
1 Equipment Toby 1
2 Inventory Carl 1
3 Stocks Guest 2
4 Portfolios Guest 3
Reports
REP_ID DESC GROUP_ID
-----------------------------------
1 Test 1
2 Today 1
3 Last Week 2
GROUP
GROUP_ID GROUP
---------------------------------
1 Standard
2 Target Week
GROUP_CAT_JOIN
GCJ_ID GROUP_ID CAT_ID
----------------------------------
1 1 1
2 1 2
3 2 3
CATEGORIES
CAT_ID CAT
-------------------------------
1 York Office
2 Glasgow Office
3 Aberdeen Office
CAT_QUOTE_JOIN
CQJ_ID CAT_ID QUOTE_ID
-----------------------------------
1 1 1
2 2 2
3 3 3
QUOTE
QUOTE_ID QUOTE
------------------------------------
1 Booking a meeting room
2 Car Park Policy
3 New User Guide
This is the test data I am using at the moment and to my knowledge it is similar to what will be run through once this is done. In all honesty I'm still trying to get my head around this structure.
The result I am looking for is if I choose to search by group I'll get everything within a group, if I choose everything inside a category I get everything just inside that category, and if I choose something from a category in a group I get everything inside that category. The problem at the moment is that whenever the group is referenced everything inside every category that's linked to the group is pulled.
The following will get the necessary rows from the results:
select
a.*
from
results a
inner join reports b on
a.rep_id = b.rep_id
and (-1 = #GroupID or
b.group_id = #GroupID)
and (-1 = #CatID or
b.cat_id = #CatID)
Note that I used -1 as the placeholder for all Groups and Categories. Obviously, use a value that makes sense to you. However, this way, you can specify a specific group_id or a specific cat_id and get the results that you want.
Additionally, if you want Group/Category/Quote details, you can always append more inner joins to get that info.
Also note that I added the Group_ID and Cat_ID conditions to the Reports table. This would be the SQL necessary if and only if you add a Cat_ID column to the Reports table. I know that your current table structure doesn't support this, but it needs to. Otherwise, as my grandfather used to say, "Boy, you can't get there from here." The issue here is that you want to limit reports by group and category, but reports only knows about group. Therefore, we need to tie something to the category from reports. Otherwise, it will never, ever, ever limit reports by category. The only thing that you can limit by both group and category is quotes. And that doesn't seem to be your requirement.
As an addendum: If you add cat_id to results instead of reports, the join condition should be:
and (-1 = #CatID or
a.cat_id = #CatID)
Is this what you are looking for?
SELECT a.*
FROM Results a
JOIN Reports b ON a.REP_Id = c.REP_Id
WHERE EXISTS (
SELECT * FROM CAT_QUOTE_JOIN c
WHERE c.QUOTE_ID = b.QUOTE_ID -- correlation to the outer query
AND c.CAT_ID = #CAT_ID -- parameterization
)
OR EXISTS (
-- note that subquery table aliases are not visible to other subqueries
-- so we can reuse the same letters
SELECT * FROM CAT_QUOTE_JOIN c, GROUP_CAT_JOIN d
WHERE c.CAT_ID = d.CAT_ID -- subquery join
AND c.QUOTE_ID = b.QUOTE_ID -- correlation to the outer query
AND d.GROUP_ID = #GROUP_ID -- parameterization
)

Resources