SQL select and Group by - sql

I have a table in SQL like this.
OrderID ItemID ItemPrice ItemType
1 A 100 1
1 B 50 1
1 C 10 0
2 A 100 1
2 F 60 0
3 G 10 0
So I want to get out put like this?
OrderID ItemPrice -Type= 1 ItemPrice -Type= 0
1 150 10
2 10 60
3 10
Do you have any idea about the SQL command to use?
I think it is group by order ID and Item type.

What you are doing is a pivot transformation. There are a few ways to do it, but my favorite way is using CASE inside SUM:
SELECT
OrderId,
SUM(CASE WHEN ItemType = 0 THEN ItemPrice ELSE 0 END) AS Type0_Price,
SUM(CASE WHEN ItemType = 1 THEN ItemPrice ELSE 0 END) AS Type1_Price
FROM MyTable
GROUP BY OrderId
This scales nicely if you have more than two types. All you have to do is add another SUM(...) line in your select list without having to change the rest of the query.
I think this will perform well, since the calculations in the SELECT list can be done without incurring additional row scans or lookups. That's the downside of self-joins and sub-selects.

Try this::
Select
DISTINCT(orderId),
(Select SUM(ITEMPRICE) from table where Itemtype=1 group by ORDERID) as ItemType1,
(Select SUM(ITEMPRICE) from table where Itemtype=0 group by ORDERID) as ItemType0
from table

Untested, but this should work for you.
SELECT t1.OrderID,
ItemPrice-Type1 = SUM(t1.ItemPrice),
ItemPrice-Type2 = SUM(t2.ItemPrice)
FROM TableName t1
INNER JOIN TableName t2 on t1.OrderID = t2.OrderID and t1.ItemID = t2.ItemID
WHERE t1.ItemType = 1 AND t2.ItemType = 0
GROUP BY t1.OrderID

Did this work?:
SELECT
OrderID,
SUM(ItemPrice*ItemType) Type1,
SUM(ItemPrice*(1-ItemType)) Type0
FROM
TableName
GROUP BY OrderID

Related

Querying a subset

I want to write an SQL query to find records which contain a particular column and from that subset want to find records which doesn't contain a some other value. How do you write a query for that?
cid id2 attribute
--------------------------------
1 100 delete
1 100 payment
1 100 void
2 100 delete
2 102 payment
2 102 void
3 102 delete
3 103 payment
In above example, I want to list cid for which payment and delete attributes exist but void attribute doesn't exist. So it should list out 3 from above example because it doesn't have void attribute.
Forgot to mention that there could be more attributes. However, I need to list out records for which delete and payment exist regardless of other attributes but void doesn’t.
I call this a "set-within-sets" query, because you are looking for particular sets of attributes within each cid.
I would express this with group by and conditions in the having:
select cid
from t
group by cid
having sum(case when attribute = 'payment' then 1 else 0 end) > 0 and
sum(case when attribute = 'delete' then 1 else 0 end) > 0 and
sum(case when attribute = 'void' then 1 else 0 end) = 0 ;
In some databases, you can simplify this with string aggregation -- assuming there are no duplicate attributes for cids. For instance, using the MySQL function:
select cid
from t
where attribute in ('payment', 'delete' 'void')
group by cid
having group_concat(attribute order by attribute) = 'delete,payment';
You can use conditional aggregation:
select cid
from tablename
where attribute in ('delete', 'payment', 'void')
group by cid
having
count(distinct attribute) = 2
and
sum(
case attribute
when 'void' then 1
else 0
end
) = 0
If there are not more attributes than these 3, then you can omit the WHERE clause.
See the demo.
Results:
| cid |
| --- |
| 3 |
I'm assuming that there are only three attributes, so the logic behind this query is:
First COUNT the number of attributes GROUP BY cid, and then LEFT JOIN the original table ON attribute is void. You should grab cid that has exactly 2 attributes and no void.
The original table is named as temp:
SELECT
subq2.result_cid
FROM (
SELECT
*
FROM (
SELECT
T.cid AS result_cid,
COUNT(T.attribute) AS count
FROM
temp AS T
GROUP BY
T.cid
) AS subq
LEFT OUTER JOIN temp AS T2 ON subq.result_cid = T2.cid AND T2.attribute = 'void'
) AS subq2
WHERE subq2.count = 2 AND subq2.id2 IS NULL
use corelated subquery by using not exists
select t1.* from tablename t1
where not exists( select 1 from tablename t2
where t1.cid=t2.cid and attribute='void'
)
and exists ( select 1 from tablename t2
where t1.cid=t2.cid
having count(distinct attribute)=2
)
and attribute in ('payment','delete')
demo online

Sql query to get

I have a table that contains list of items contained in a package
PackageId, ItemId
One package may contain more than 1 item.
I want to get a list or count of packages that contains lets say Items 1 and 2
How would I do that?
One way is to group on (packageid) and demand that each group has both items:
select packageid
from YourTable
group by
packageid
having max(case when itemid = 1 then 1 end) = 1
and max(case when itemid = 2 then 1 end) = 1
Another is the intersect operator. Demand that the package is in both sets:
select packageid
from YourTable
where itemid = 1
intersect
select packageid
from YourTable
where itemid = 2
Yet another way with an inner join. For each row with item 1, demand that there is another row for the same package with item 2:
select distinct yt1.packageid
from YourTable yt1
join YourTable yt2
on yt1.packageid = yt2.packageid
where yt1.itemid = 1
and yt2.itemid = 2

How to reolve Aggregate function on an expression containing an aggregate or a subquery

Query:
SELECT * FROM TRIALTABLE1
Output :
PRODUCTNAME PRICE
BMW 2000000
Yamaha R15 125000
Splendour Plus 60000
BMW 7000000
Query #2:
select * from TRIALTABLE2
Output:
SRNO PRODUCTNAME
1 Splendour Plus
2 BMW
If my query is static as
select
PRODUCTNAME, sum(CASE when PRODUCTNAME='BMW' then 10 else 0 END ) as ID
from TRIALTABLE1
group by PRODUCTNAME
it works.. But If I use dynamic PRODUCTNAME for BMW, it throws error..
select
PRODUCTNAME, sum(CASE when PRODUCTNAME= (SELECT PRODUCTNAME FROM TRIALTABLE2 WHERE SRNO=2) then 10 else 0 END ) as ID
from TRIALTABLE1
group by PRODUCTNAME
Error:
Lookup Error - SQL Server Database Error: Cannot perform an aggregate function on an expression containing an aggregate or a subquery
How should I resolve this problem ?
Well, from your sample, it seems that you just need a left join to avoid the subquery ?
select t1.productname,
sum (case when t2.srno= 2 then 10 else 0 end) as ID
from trialtable1 t1
left join trialtable2 t2 on t2.productname= t1.productname
group by t1.productname
As a note, you can write this query as:
select distinct PRODUCTNAME,
(SELECT sum(case when srno = 2 then 10 else 0 end)
FROM TRIALTABLE2 t2
WHERE t2.PRODUCTNAME = t1.PRODUCTNAME
) as ID
from TRIALTABLE1;
This is to emphasize that although you cannot use a subquery inside an aggregation function, you can use an aggregation function inside a subquery.

Get the distinct count of values from a table with multiple where clauses

My table structure is this
id last_mod_dt nr is_u is_rog is_ror is_unv
1 x uuid1 1 1 1 0
2 y uuid1 1 0 1 1
3 z uuid2 1 1 1 1
I want the count of rows with:
is_ror=1 or is_rog =1
is_u=1
is_unv=1
All in a single query. Is it possible?
The problem I am facing is that there can be same values for nr as is the case in the table above.
Case statments provide mondo flexibility...
SELECT
sum(case
when is_ror = 1 or is_rog = 1 then 1
else 0
end) FirstCount
,sum(case
when is_u = 1 then 1
else 0
end) SecondCount
,sum(case
when is_unv = 1 then 1
else 0
end) ThirdCount
from MyTable
you can use union to get multiple results e.g.
select count(*) from table with is_ror=1 or is_rog =1
union
select count(*) from table with is_u=1
union
select count(*) from table with is_unv=1
Then the result set will contain three rows each with one of the counts.
Sounds pretty simple if "all in a single query" does not disqualify subselects;
SELECT
(SELECT COUNT(DISTINCT nr) FROM table1 WHERE is_ror=1 OR is_rog=1) cnt_ror_reg,
(SELECT COUNT(DISTINCT nr) FROM table1 WHERE is_u=1) cnt_u,
(SELECT COUNT(DISTINCT nr) FROM table1 WHERE is_unv=1) cnt_unv;
how about something like
SELECT
SUM(IF(is_u > 0 AND is_rog > 0, 1, 0)) AS count_something,
...
from table
group by nr
I think it will do the trick
I am of course not sure what you want exactly, but I believe you can use the logic to produce your desired result.

Subselect Query Improvement

How can I improve the SQL query below (SQL Server 2008)? I want to try to avoid sub-selects, and I'm using a couple of them to produce results like this
StateId TotalCount SFRCount OtherCount
---------------------------------------------------------
AZ 102 50 52
CA 2931 2750 181
etc...
SELECT
StateId,
COUNT(*) AS TotalCount,
(SELECT COUNT(*) AS Expr1 FROM Property AS P2
WHERE (PropertyTypeId = 1) AND (StateId = P.StateId)) AS SFRCount,
(SELECT COUNT(*) AS Expr1 FROM Property AS P3
WHERE (PropertyTypeId <> 1) AND (StateId = P.StateId)) AS OtherCount
FROM Property AS P
GROUP BY StateId
HAVING (COUNT(*) > 99)
ORDER BY StateId
This may work the same, hard to test without data
SELECT
StateId,
COUNT(*) AS TotalCount,
SUM(CASE WHEN PropertyTypeId = 1 THEN 1 ELSE 0 END) as SFRCount,
SUM(CASE WHEN PropertyTypeId <> 1 THEN 1 ELSE 0 END) as OtherCount
FROM Property AS P
GROUP BY StateId
HAVING (COUNT(*) > 99)
ORDER BY StateId
Your alternative is a single self-join of Property using your WHERE conditions as a join parameter. The OtherCount can be derived by subtracting the TotalCount - SFRCount in a derived query.
Another alternative would be to use the PIVOT function like this:
SELECT StateID, [1] + [2] AS TotalCount, [1] AS SFRCount, [2] AS OtherCount
FROM Property
PIVOT ( COUNT(PropertyTypeID)
FOR PropertyTypeID IN ([1],[2])
) AS pvt
WHERE [1] + [2] > 99
You would need to add an entry for each property type which could be daunting but it is another alternative. Scott has a great answer.
If PropertyTypeId is not null then you could do this with a single join. Count is faster than Sum. But is Count plus Join faster than Sum. The test case below mimics your data. docSVsys has 800,000 rows and there are about 300 unique values for caseID. The Count plus Join in this test case is slightly faster than the Sum. But if I remove the with (nolock) then Sum is about 1/4 faster. You would need to test with your data.
select GETDATE()
go;
select caseID, COUNT(*) as Ttl,
SUM(CASE WHEN mimeType = 'message/rfc822' THEN 1 ELSE 0 END) as SFRCount,
SUM(CASE WHEN mimeType <> 'message/rfc822' THEN 1 ELSE 0 END) as OtherCount,
COUNT(*) - SUM(CASE WHEN mimeType = 'message/rfc822' THEN 1 ELSE 0 END) as OtherCount2
from docSVsys with (nolock)
group by caseID
having COUNT(*) > 1000
select GETDATE()
go;
select docSVsys.caseID, COUNT(*) as Ttl
, COUNT(primaryCount.sID) as priCount
, COUNT(*) - COUNT(primaryCount.sID) as otherCount
from docSVsys with (nolock)
left outer join docSVsys as primaryCount with (nolock)
on primaryCount.sID = docSVsys.sID
and primaryCount.mimeType = 'message/rfc822'
group by docSVsys.caseID
having COUNT(*) > 1000
select GETDATE()
go;