SQL query getting data - sql

In SQL Server 2000:
hello i have a table with the following structure:
sku brand product_name inventory_count
------ ------ ------------- ---------------
c001 honda honda car 1 3
t002 honda honda truck 1 6
c003 ford ford car 1 7
t004 ford ford truck 1 8
b005 honda honda bike 5 9
b006 ford ford bike 6 18
I'm using the following SQL query
select distinct left(sku,1) from products
this would return the following:
c
t
b
and then ...
c = car
t = truck
b = bike
this works great,
Now I want to get just one product example for each of the categories with the greatest INVENTORY_COUNT
so that it returns the data as:
c, "ford car 1"
t, "ford truck 1"
b, "ford bike 6"
what SQL query would i run to get that data??
i want the item with the greatest INVENTORY_COUNT for each category.. left(sku,1)
thanks!!

You could join the table on itself to filter out the rows with less than maximum inventory:
select left(a.sku,1), max(a.product_name), max(a.inventory_count)
from YourTable a
left join YourTable more_inv
on left(a.sku,1) = left(more_inv.sku,1)
and a.inventory_count < more_inv.inventory_count
where more_inv.sku is null
group by left(a.sku,1)
The WHERE condition on more_inv.sku is null filters out rows that don't have the highest inventory for their one letter category.
Once we're down to rows with the maximum inventory, you can use max() to get the inventory_count (it'll be the same for all rows) and another max() to get one of the products with the highest inventory_count. You could use min() too.

im using the following sql query which works,
SELECT DISTINCT left(field1,1) as cat , MAX(sku) as topproduct FROM products where inventory_count > 0 GROUP BY left(sku,1)
i just need to add in there an ..order by inventory_count

Using SQL Server 2005 you can try this
DECLARe #Table TABLE(
sku VARCHAR(50),
brand VARCHAR(50),
product_name VARCHAR(50),
inventory_count INT
)
INSERT INTO #Table SELECT 'c001', 'honda', 'honda car 1', 3
INSERT INTO #Table SELECT 't002', 'honda', 'honda truck 1', 6
INSERT INTO #Table SELECT 'c003', 'ford', 'ford car 1', 7
INSERT INTO #Table SELECT 't004', 'ford', 'ford truck 1', 8
INSERT INTO #Table SELECT 'b005', 'honda', 'honda bike 5', 9
INSERT INTO #Table SELECT 'b006', 'ford', 'ford bike 6', 18
SELECT LEFT(sku,1),
product_name
FROM (
SELECT *,
ROW_NUMBER() OVER( PARTITION BY LEFT(sku,1) ORDER BY inventory_count DESC) ORDERCOUNT
FROm #Table
) SUB
WHERE ORDERCOUNT = 1
OK Then you can try
SELECT LEFT(sku,1),
*
FROm #Table t INNER JOIN
(
SELECT LEFT(sku,1) c,
MAX(inventory_count) MaxNum
FROM #Table
GROUP BY LEFT(sku,1)
) sub ON LEFT(t.sku,1) = sub.c and t.inventory_count = sub.MaxNum

For mysql:
SELECT LEFT(sku,1), product_name FROM Table1 GROUP BY LEFT(sku,1)
For MS SQL 2005 (maybe also works in 2000?):
SELECT LEFT(sku,1), MAX(product_name) FROM Table1 GROUP BY LEFT(sku,1)

Try this
declare #t table (sku varchar(50),brand varchar(50),product_name varchar(50),inventory_count int)
insert into #t
select 'c001','honda','honda car 1',3 union all
select 't002','honda','honda truck 1',6 union all
select 'c004','ford','ford car 1',7 union all
select 't004','ford','ford truck 1',8 union all
select 'b005','honda','honda bike 5',9 union all
select 'b006','ford','ford bike 6',18
Query:
select
x.s + space(2) + ',' + space(2) + '"' + t.product_name + '"' as [Output]
from #t t
inner join
(
SELECT left(sku,1) as s,MAX(inventory_count) ic from #t
group by left(sku,1)
) x
on x.ic = t.inventory_count
--order by t.inventory_count desc
Output
c , "ford car 1"
t , "ford truck 1"
b , "ford bike 6"

In general, might there not be more than one item with max(inventory_count)?
To get max inventory per cateogry, use a subquery, (syntax will depend on your database):
SELECT LEFT(sku,1) as category, MAX(inventory_count) as c
FROM Table1
GROUP BY LEFT(sku,1)
SORT BY LEFT(sku,1)
This will give you a table of max_inventory by category, thus:
b,18
c,7
t,8
So now you know the max per category. To get matching products, use this result as
a subquery and find all products in the given cateogry that match the given max(inventory_count):
SELECT t1.*
FROM Table1 AS t1,
(SELECT LEFT(sku,1) AS category, MAX(inventory_count) AS c
FROM Table1
GROUP BY LEFT(sku,1)
) AS t2
WHERE LEFT(t1.sku,1) = t2.category AND t2.c = t1.inventory_count
Sorry, the code above may/may not work in your database, but hope you get the idea.
Bill
PS -- probably not helpful, but the table design isn't really helping you here. If you have control over the schema, would help to separate this into multiple tables.

Related

SQL for selecting values in a single column by 'AND' condition

I have a table data like bellow
PersonId
Eat
111
Carrot
111
Apple
111
Orange
222
Carrot
222
Apple
333
Carrot
444
Orange
555
Apple
I need an sql query which return the total number of PersonId's who eat both Carrot and Apple.
In the above example the result is, Result : 2. (PersonId's 111 and 222)
An ms-sql query like 'select count(distinct PersonId) from Person where Eat = 'Carrot' and Eat = 'Apple''
You can actually get the count without using a subquery to determine the persons who eat both. Assuming that the rows are unique:
select ( count(distinct case when eat = 'carrot' then personid end) +
count(distinct case when eat = 'apple' then personid end) -
count(distinct personid)
) as num_both
from t
where eat in ('carrot', 'apple')
SELECT PersonID FROM Person WHERE Eat = 'Carrot'
INTERSECT
SELECT PersonID FROM Person WHERE Eat = 'Apple'
You can use conditional aggregation of a sort:
select
personid
from <yourtable>
group by
personid
having
count (case when eat = 'carrot' then 1 else null end) >= 1
and count (case when eat = 'apple' then 1 else null end) >= 1
At this example, I use STRING_AGG to make easy the count and transform 'Apple' and 'Carrot' to one string comparison:
create table #EatTemp
(
PersonId int,
Eat Varchar(50)
)
INSERT INTO #EatTemp VALUES
(111, 'Carrot')
,(111, 'Apple')
,(111, 'Orange')
,(222, 'Carrot')
,(222, 'Apple')
,(333, 'Carrot')
,(444, 'Orange')
,(555, 'Apple')
SELECT Count(PersonId) WhoEatCarrotAndApple FROM
(
SELECT PersonId,
STRING_AGG(Eat, ';')
WITHIN GROUP (ORDER BY Eat) Eat
FROM #EatTemp
WHERE Eat IN ('Apple', 'Carrot')
GROUP BY PersonId
) EatAgg
WHERE Eat = 'Apple;Carrot'
You can use EXISTS statements to achieve your goal. Below is a full set of code you can use to test the results. In this case, this returns a count of 2 since PersonId 111 and 222 match the criteria you specified in your post.
CREATE TABLE Person
( PersonId INT
, Eat VARCHAR(10));
INSERT INTO Person
VALUES
(111, 'Carrot'), (111, 'Apple'), (111, 'Orange'),
(222, 'Carrot'), (222, 'Apple'), (333, 'Carrot'),
(444, 'Orange'), (555, 'Apple');
SELECT COUNT(DISTINCT PersonId)
FROM Person AS p
WHERE EXISTS
(SELECT 1
FROM Person e1
WHERE e1.Eat = 'Apple'
AND p.PersonId = e1.PersonId)
AND EXISTS
(SELECT 1
FROM Person e1
WHERE e1.Eat = 'Carrot'
AND p.PersonId = e1.PersonId);
EXISTS statements have a few advantages:
No chance of changing the granularity of your data since you aren't joining in your FROM clause.
Easy to add additional conditions as needed. Just add more EXISTS statements in your WHERE clause.
The condition is cleanly encapsulated in the EXISTS, so code intent is clear.
If you ever need complex conditions like existence of a value in another table based on specific filter conditions, then you can easily add this without introducing table joins in your main query.
Some alternative solutions such as PersonId IN (SUBQUERY) can introduce unexpected behavior in certain conditions, particularly when the subquery returns a NULL value.
select
count(PersonID)
from Person
where eat = 'Carrot'
and PersonID in (select PersonID
from Person
where eat = 'Apple');
Only selecting those persons who eat apples, and from that result select all those that eat carrots too.
SELECT COUNT (A.personID) FROM
(SELECT distinct PersonID FROM Person WHERE Eat = 'Carrot'
INTERSECT
SELECT distinct PersonID FROM Person WHERE Eat = 'Apple') as A

Get the sum of details table

I need to retrieve a master record together the sum of its details. Here are the definitions of two tables:
Master table (Master)
ID
Name
Example record:
10 Product
Details table (Details)
master_id
type (values can only be red or blue)
quantity
Example records:
10 red 2
10 red 5
10 red 6
10 blue 7
10 blue 9
I need to have such results:
10 Product 13 (sum for red) 16 (sum for blue)
The database is SQL Server 2017. How can I write a SINGLE query without using a stored procedure?
---UPDATE---
Based on the input from Venkataraman R, here is the solution:
SELECT m.id, m.name ,
SUM(CASE when type='red' then quantity end) as redsum,
SUM(CASE when type='blue' then quantity end) as bluesum
from mydetails as t
inner join mymaster as m
on m.id = t.master_id
GROUP BY m.id, m.name
You can use CONCAT function with group by to get results as single statement.
DECLARE #table table(master_id int, typev varchar(10), quantity int)
DECLARE #master table(id int, name varchar(50))
insert into #master values(10, 'Product10')
insert into #table values
(10,'red', 2),
(10,'red', 5),
(10,'red', 6),
(10,'blue', 7),
(10,'blue', 9);
SELECT m.name ,
SUM(CASE when typev='red' then quantity end) as redsum,
SUM(CASE when typev='blue' then quantity end) as bluesum
from #table as t
inner join #master as m
on m.id = t.master_id
GROUP BY m.name
+-----------+--------+---------+
| name | redsum | bluesum |
+-----------+--------+---------+
| Product10 | 13 | 16 |
+-----------+--------+---------+
If I get you correctly then you are looking for pivot, in that case you can use case expressions as following
select
master_id,
sum(case when type = 'red' then quantity end) as sum_of_red,
sum(case when type = 'blue' then quantity end) as sum_of_blue
from yourTable
group by
master_id
If you simply wants total quantity by type then use the following
select
master_id,
type,
sum(quantity) as total_quantity
from yourTable
group by
master_id,
type
Just use a group by
Select master_id, type, sum(quantity)
From details group by master_id, type
And use pivot to show each product in one line
With data_cte as
(Select master_id, type, sum(quantity)
From details group by master_id, type)
Select * from data_cte pivot (
For type in ('red', 'black'))

How to the result set form the below table

Table 1 contains certain set of data's. I need to get the following result set form the Table 1
Table1
Id Desc ParentId
1 Cloths 0
2 Mens 1
3 Womens 1
4 T-Shirt_M 2
5 Casual Shirts_M 2
6 T-Shirt_F 3
7 Education 8
If I pass a parameter as "Casual Shirts_M" I should get the below result set.
Result Set
Id Desc ParentId
1 Cloths 0
2 Mens 1
5 Casual Shirts_M 2
As mentioned in comments, there are plenty of Recursive Common Table Expressions examples for this, here's another one
DECLARE #Desc NVARCHAR(50) = 'Casual Shirts_M'
;WITH cteX
AS
( SELECT
B.Id, B.[DESC], B.ParentId
FROM
Table1 b
WHERE
B.[Desc] = #Desc
UNION ALL
SELECT
E.Id, E.[DESC], E.ParentId
FROM
Table1 E
INNER JOIN
cteX r ON e.Id = r.ParentId
)
SELECT * FROM cteX ORDER BY ID ASC
SQL-Fiddle provided by #WhatsThePoint
The question comes under the concept of Building hierarchy using Recursive CTE:
CREATE TABLE cloths
(
id INT,
descr VARCHAR(100),
parentid INT
);
insert into cloths values (1,'Cloths',0);
insert into cloths values (2,'Mens',1);
insert into cloths values (3,'Womens',1);
insert into cloths values (4,'T-Shirt_M',2);
insert into cloths values (5,'Casual Shirts_M',2);
insert into cloths values (6,'T-Shirt_F',3);
insert into cloths values (7,'Education',8);
DECLARE #variety VARCHAR(100) = 'Casual Shirts_M';
WITH
cte1 (id, descr, parentid)
AS (SELECT *
FROM cloths
WHERE descr = #variety
UNION ALL
SELECT c.id,
c.descr,
c.parentid
FROM cloths c
INNER JOIN cte1 r
ON c.id = r.parentid)
SELECT *
FROM cte1
ORDER BY parentid ASC;

Adding value to column depending on LIKE

I have SSMS 2008 R2. This is my table
PK | DateOf | Other Columns | Items
01 | 05/30/2017 15:30 | Blah | truck, elephant, apple, . . .
02 | 04/15/2012 07:07 | Bluh | foot, orange, horse, . . .
03 | 11/01/2016 10:30 | Wham | apple, screen, penny, . . .
I am trying to search the Items column for each record and count how many times the fruits occur. Conveniently enough, there will only be a single fruit for each record, but it would be cool if the solution would be able to handle several fruits (maybe even duplicates). The result table would look like this for the above
Count | Fruit
2 | Apple
1 | Orange
I have a complete list of the "fruits". I was trying to figure out how to make it work with LIKE's.
SELECT
count(PK) AS [Count]
??? AS [Fruit]
WHERE DateOf >= '2011-01-01 00:00' AND DateOf < '2012-01-01 00:00'
AND Items LIKE '%Apple%' --??(some kind of code that looks for the fruit values??)
This is the wrong way to store lists. But, sometimes we are stuck with other people's really bad design decisions.
In this case, you want a split() function. SQL Server 2016 offers one. You can also find code for one on the web (Google "SQL Server split string"):
SELECT ss.fruit, count(*) AS [Count]
FROM t CROSS APPLY
string_split(t.items) ss(fruit)
WHERE DateOf >= '2011-01-01' AND DateOf < '2012-01-01'
GROUP BY ss.fruit;
You can use a simple outer apply to identify fruits if you have table with fruits. Like this:
drop table if exists dbo.Details;
drop table if exists dbo.Fruits;
create table dbo.Details (
PK int not null primary key
, DateOf datetime2(3)
, Items varchar(100)
);
create table dbo.Fruits (
Fruit varchar(100) not null primary key
);
insert into dbo.Details (PK, DateOf, Items)
values (1, '20170530 15:30', 'truck, elephant, apple, . . .')
, (2, '20120415 07:07', 'foot, orange, horse, . . .')
, (3, '20161101 10:30', 'apple, screen, penny, orange, . . .')
insert into dbo.Fruits (Fruit)
values ('apple'), ('orange');
select
d.PK, d.DateOf, d.Items, count(*) as CountOfFruit
from dbo.Details d
outer apply (
select
*
from dbo.Fruits f
where d.Items like '%' + f.Fruit + '%'
) tf
group by d.PK, d.DateOf, d.Items
Try this it may helps you
IF OBJECT_ID('Tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
DECLARE #SearchWords nvarchar(max)='Apple,Orange'
DECLARE #Table TABLE (SearchWords nvarchar(max))
INSERT INTO #Table
SELECT #SearchWords
;With cte_Search
AS
(
SELECT ROW_NUMBER()OVER(ORDER BY (SELECT 1) )AS Seq,
Split.a.value('.', 'VARCHAR(1000)') AS SearchWords
FROM (
SELECT
CAST('<S>' + REPLACE(SearchWords, ',', '</S><S>') + '</S>' AS XML) AS SearchWords
FROM #Table
) AS A
CROSS APPLY SearchWords.nodes('/S') AS Split(a)
)
SELECT * INTO #Temp FROM cte_Search
;With CTE (PK , DateOf , OtherColumns , Items)
AS
(
SELECT 01 , '05/30/2017 15:30' ,'Blah', 'truck, elephant, apple'UNION ALL
SELECT 02 , '04/15/2012 07:07' ,'Bluh', 'foot, orange, horse' UNION ALL
SELECT 03 , '11/01/2016 10:30' ,'Wham', 'apple, screen, penny'
)
SELECT SearchWords AS Fruit,
MAX(Seq) AS CountOFFruits FROM
(
SELECT C.OtherColumns,
t.SearchWords,
C.Items,
COUNT(C.Items) CntItems,
ROW_NUMBER()OVER(Partition by t.SearchWords order by C.OtherColumns ) AS Seq
FROM CTE c ,#Temp t
WHERE CHARINDEX(t.SearchWords,c.Items) >0
Group by C.OtherColumns,t.SearchWords,C.Items
)dt
Group by SearchWords
Result
Fruit CountOFFruits
----------------------
Apple 2
Orange 1

Picking info using junction table(SQL SERVER 2005) [ SET BASED]

I have 3 tables
1) tblPurchaser having 2 columns:
PurchaserId PurchaserName
1 A1
2 A2
3 A3
2) tblCar having 2 columns:
CarId Carname
11 C1
12 C2
13 C3
14 C4
And the last is a junction table tblInformation where the information about those persons are given who has purchased cars.
PurchaserId CarId
1 11
1 12
2 11
2 13
Now I need to write a set based query where I can be able to obtain the information of those cars which has not been purchased by the persons
Desired Output
PurchaserId CarId
1 13
1 14
2 12
2 14
3 11
3 12
3 13
3 14
Note: This is a real time problem which I am implementing in my project. Because of privacy of company, I have changed the tables and information. But my situation is something similar
Please help me
Edited
So far I have written this query:
SELECT 1 as purchaserid,carid from tblcar
where carid not in (select carid from tblinformation where purchaserid = 1)
union all
SELECT 2 as purchaserid,carid from tblcar
where carid not in (select carid from tblinformation where purchaserid = 2)
union all
SELECT 3 as purchaserid,carid from tblcar
where carid not in (select carid from tblinformation where purchaserid = 3)
But as you can make out that i am hardcoding the purchaserid's. And also in real time I will not know how many id's will be there. So everything has to be done at runtime.
Please helpenter code here
Clue: NOT EXISTS
You should really try to do some homework yourself... 3rd question today...
LEFT JOIN ... WHERE ... IS NULL to the rescue:
SELECT tblPurchaser.PurchaserId, tblCar.CarId
FROM tblPurchaser JOIN tblCar
LEFT JOIN tblInformation ON(
tblPurchaser.PurchaserId = tblInformation.PurchaserId
AND tblCar.CarId = tblInformation.CarId)
WHERE tblInformation.CarId IS NULL
Try this
select pur.PurchaserId, car.CarId
from tblPurchaser pur, tblCar car
where not exists (select 1 from tblInformation where PurchaserId = pur. PurchaserId and CarId = car. CarId)
order by pur.PurchaserId;
Try this:
SELECT PurchaserID, CarID
FROM Purchasers
CROSS JOIN Cars
EXCEPT
SELECT *
FROM tblInformation
Here is a SQL script that demonstrates that this technique works correctly:
declare #soPurchaser table(PurchaserId int, PurchaserName varchar(4));
insert #soPurchaser select 1,'A1'
insert #soPurchaser select 2,'A2'
insert #soPurchaser select 3,'A3'
Declare #SOtblCar table(CarId int, Carname varchar(4))
insert #SOtblCar select 11,'C1'
insert #SOtblCar select 12,'C2'
insert #SOtblCar select 13,'C3'
insert #SOtblCar select 14,'C4'
Declare #SOtblInfo table(PurchaserId int, CarId int)
insert #SOtblInfo select 1,11
insert #SOtblInfo select 1,12
insert #SOtblInfo select 2,11
insert #SOtblInfo select 2,13
SELECT PurchaserID, CarID
FROM #soPurchaser
CROSS JOIN #SOtblCar
EXCEPT
SELECT *
FROM #SOtblInfo
The SQL Set operators (UNION, INTERSECT, and EXCEPT) all operate on two table-sets. You will note that they have no way to map the columns from one set to the other. In all cases in SQL when column must be mapped to each other, but there is no syntax to do it explicitly, then they are always mapped based on column order.
So in this one case, if you have one of the table's column order wrong, then it will not work correctly.