Match name to ID and replace on insert - sql

Imagine an insert that looks like this:
INSERT INTO Inventory VALUES ('Product Name 1', 'Inventoried Location', Count),
('Product Name 2', 'Inventoried Location', Count),
('Product Name 3', 'Inventoried Location', Count),
...
('Product Name 1000', 'Inventoried Location', Count),
('Product Name 1001', 'Inventoried Location', Count)
Is there a way to match the Product Name to its ID in a Products table and substitute the ID for the name on insert? Needs to work for thousands of entries on the insert.

Performance would be pretty awful on this, but if you're stuck with that particular format (say, from an Excel spreadsheet or something) you can write this as:
INSERT INTO Inventory VALUES
(SELECT ProductID FROM Product WHERE ProductName = 'Product Name 1', 'Inventoried Location', 5),
(SELECT ProductID FROM Product WHERE ProductName = 'Product Name 2', 'Inventoried Location', 5),
(SELECT ProductID FROM Product WHERE ProductName = 'Product Name 3', 'Inventoried Location', 5),
...
(SELECT ProductID FROM Product WHERE ProductName = 'Product Name 1000', 'Inventoried Location', 5),
(SELECT ProductID FROM Product WHERE ProductName = 'Product Name 1001', 'Inventoried Location', 5)
Of course, this will break if you have multiple product entries with the same ProductName ...
If you want some better performance, you could insert the values into a temporary table, then do the lookup from that in a similar way to how Siyual suggested, e.g.,
INSERT INTO #tmpInventory (ProductName, ProductLocation, Number) VALUES
('Product Name 1', 'Inventoried Location', 5),
('Product Name 2', 'Inventoried Location', 5),
('Product Name 3', 'Inventoried Location', 5),
.......
INSERT INTO INVENTORY (ProductID, ProductLocation, Number)
SELECT p.ProductID, t.ProductLocation, t.Number
FROM #tmpInventory t
INNER JOIN Product p on t.ProductName = p.ProductName

You can do something like this:
Insert Inventory
Select ProductId, 'Inventoried Location', 5
From Products
Where ProductName = 'Product Name'

Related

Cant read count(*) value in temporary table

I want to fill a temporary table with values from another table and a count value, but it always throws the error:
Column name or number of supplied values does not match table
definition
My code looks like this:
CREATE TABLE #TempTable
([ObjectId] uniqueidentifier, [ListName] nvarchar(255), [HowMany] int)
INSERT INTO #TempTable
SELECT [ObjectId]
,[ListName]
,(SELECT COUNT(*) FROM [ATable] as a WHERE [ObjectId] = a.FK_ObjectId ) AS [HowMany]
FROM [AnotherTable]
It works fine for the other columns but not for the [HowMany] column.
I already tried to cast the SELECT count(*) to an integer but that also did not work.
What am I doing wrong?
I'm checking your request, I think that query below will help you, take a look please, I didn't use a temp table but is just the update the table names to be able to use a temp table.
As you didn't show us the structure of your table, I also made an example to make it simpler to understand, I think :-).
DECLARE #Category as table (
id int,
category nvarchar(50)
);
DECLARE #Product as table (
id int,
idCategory int,
productName nvarchar(50)
);
INSERT INTO #Category
VALUES
(1, 'Category 1'),
(2, 'Category 2'),
(3, 'Category 3')
INSERT INTO #Product
VALUES
(1,1, 'Product 1'),
(2,1, 'Product 2'),
(3,1, 'Product 3'),
(4,2, 'Product 4'),
(5,2, 'Product 5'),
(6,2, 'Product 6'),
(7,2, 'Product 7'),
(8,2, 'Product 8'),
(9,3, 'Product 9'),
(10,3, 'Product 10'),
(11,3, 'Product 11'),
(12,3, 'Product 12')
--To know how many product is used by category
SELECT * FROM
(
SELECT
C.*,
(Count(P.id) over (partition by C.ID order by C.category)) as HowManyProductByCategory
FROM
#Category as C
INNER JOIN #Product as P ON C.id = P.idCategory
) as T
GROUP BY T.id, T.category, T.HowManyProductByCategory
The result
id category HowManyProductByCategory
1 Category 1 3
2 Category 2 5
3 Category 3 4
Best Regards

SQL count the length of blocks of identical numbers, while ignoring blocks of changing numbers

CREATE TABLE identicalblocks
(
[sortid] int,
[product] varchar(57),
[changing] int
)
;
INSERT INTO identicalblocks ([sortid], [product], [changing])
VALUES
(1, 'product a', 0),
(2, 'product a', 3),
(3, 'product a', 0),
(4, 'product a', 7),
(5, 'product a', 7),
(6, 'product a', 7),
(7, 'product a', 7),
(8, 'product a', 0),
(9, 'product a', 0),
(10, 'product a', 1),
(11, 'product a', 3),
(12, 'product a', 1),
(13, 'product a', 0),
(14, 'product a', 0),
(1, 'product b', 0),
(2, 'product b', 2),
(3, 'product b', 2),
(4, 'product b', 2),
(5, 'product b', 3),
(6, 'product b', 0),
(7, 'product b', 0),
(8, 'product b', 12),
(9, 'product b', 12),
(10, 'product b', 0),
(11, 'product b', 0),
(12, 'product b', 0);
I built a sqlfiddle here:
http://sqlfiddle.com/#!18/555ace
The logic that I want to implement is that when the "changing" column switches from 0 to some positive integer, I want to start counting. If at any point the integer changes, I want to abandon counting. I only want to count the block if the integer stays the same until the block ends and goes back to 0.
My desired result would be:
Product
blocklengths
product a
1
product a
4
product b
2
Explanation:
For product a, we count the first "3" as length 1, as it switched from 0 to 3 back to 0.
We next count product a's block "7" as occurring 4 times in a row, switching from 0.
We skip counting product a's last block, as it switches from 1 to 3 back to 1.
Our resulting lengths are 1 and 4.
For product b, we skip the first block as it changes from 2 to 3 right before ending back to 0.
Product b's second block is counted as 12 appears twice without changing, as length 2.
You can treat this as a gaps-and-islands problem. The simplest method I think is to assign the "islands" by using a cumulative sum of 0 values. Then aggregate and filter:
select product, sum(case when changing <> 0 then 1 else 0 end)
from (select ib.*,
sum(case when changing = 0 then 1 else 0 end) over (partition by product order by sortid) as grp
from identicalblocks ib
) ib
group by product, grp
having count(distinct changing) = 2; -- 0 and the other value
Here is a db<>fiddle.
This is an example of a gaps-and-islands problem.
There are a number of solutions, here is one.
We use LAG to get the start of each island, in this case: the previous value is 0
We use a running COUNT to get a grouping number for each island (remember that COUNT ignores nulls)
Then we simply GROUP BY the grouping number and count up how many rows we have, and exclude all groups with differing numbers
WITH WithGroupStarts AS (
SELECT *,
IsStartOfGroup = CASE WHEN LAG(changing, 1, 0) OVER (PARTITION BY ib.product
ORDER BY ib.sortid) = 0 THEN 1 END
FROM identicalblocks ib
),
WithGroups AS (
SELECT *,
GroupId = COUNT(ib.IsStartOfGroup) OVER (PARTITION BY ib.product
ORDER BY ib.sortid ROWS UNBOUNDED PRECEDING)
FROM WithGroupStarts ib
WHERE ib.changing <> 0
)
SELECT
ib.Product,
blocklengths = COUNT(*)
FROM WithGroups ib
GROUP BY
ib.Product,
ib.GroupId
HAVING MIN(ib.changing) = MAX(ib.changing);
SQL Fiddle

How to concatenate strings from multiple rows?

I have a table with the following columns:
PRODUCT
YEAR_UPDATED
Example data:
PROD1,2017
PROD1,2015
PROD2,2014
PROD3,2017
How can I get a list of when each product was updated? Something like:
PRODUCT,2017,2016,2015,2014,etc
PROD1,Y,N,Y,N
PROD2,N,N,N,Y
PROD3,Y,N,N,N
or
PROD1,2017,2015
PROD2,2014
PROD3,2017
Oracle DB
Thanks!
I am assuming the table's name is Products, change it to whatever your table's name is.
Oracle
You achieve it by using the LISTAGG function.
select p.Product || ', ' || listagg(p.YEARUPDATED,',') within group (order by p.YEARUPDATED)
from Products p
group by p.Product;
If you are using SQL Server, this is how you can do it.
select p.Product + ', ' + stuff((select ', '+ cast(tp.YearUpdated as varchar(4)) from Products tp where p.Product = tp.Product
FOR XML PATH('')) , 1, 2, '')
from Products p
group by p.Product
In case you want to quickly test it, you can try this out (using an in-memory table).
declare #Products table (Product varchar(50), YearUpdated int);
insert into #Products values ('Product 1', 2000);
insert into #Products values ('Product 1', 2001);
insert into #Products values ('Product 1', 2002);
insert into #Products values ('Product 1', 2003);
insert into #Products values ('Product 2', 2010);
insert into #Products values ('Product 2', 2011);
insert into #Products values ('Product 4', 2012);
insert into #Products values ('Product 4', 2013);
insert into #Products values ('Product 4', 2015);
insert into #Products values ('Product 3', 2005);
select p.Product + ', ' + stuff((select ', '+ cast(tp.YearUpdated as varchar(4)) from #Products tp where p.Product = tp.Product
FOR XML PATH('')) , 1, 2, '')
from #Products p
group by p.Product
Assuming you have id and year columns in your table :
select cast ( t1.id as varchar) + ',' + ( case when t1.rn2 = 1 then '2015' else '' end )
+
( case when t1.rn2 = 2 then '2015,2016 ' else '' end ) +
( case when t1.rn2 = 3 then '2015,2016,2017' else '' end )
from
(select distinct yourTBL.id , max(yourTBL.rn)
over ( partition by yourTBL.id order by yourTBL.year rows BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED following ) as rn2
from (select id , year ,
row_number()over (partition by id order by year) as rn from yourTBL ) t) t1

Sum of 2 columns if row exists in Table 2 - SQL

CREATE TABLE #Details
(
SName VARCHAR(20),
PName VARCHAR(20),
SoldCount INT,
Value INT
)
CREATE TABLE #DetailsException
(
ExSName VARCHAR(20),
ExPName VARCHAR(20)
)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 1', 'Product 1', 10,400)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 1', 'Product 3', 3,500)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 2', 'Product 1', 8,30)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 2', 'Product 2', 10,25)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 2', 'Product 2', 23,120)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 4', 'Product 1', 23,50)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 4', 'Product 3', 10,50)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 4', 'Product 5', 7,200)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 5', 'Product 1', 10,100)
INSERT INTO #Details(SName, PName, SoldCount,Value) VALUES ('Store 5', 'Product 1', 24,240)
INSERT INTO #DetailsException(ExSName, ExPName) VALUES ('Store 2', 'Product 2')
INSERT INTO #DetailsException(ExSName, ExPName) VALUES ('Store 4', 'Product 5')
SELECT SName, PName, **CASE WHEN EXISTS(SELECT 1 FROM #DetailsException WHERE ExSName = SName AND ExPName = PNAme ) THEN 0 ELSE SUM(SoldCount) END AS SoldCount**, SUM(Value) AS Value
FROM #Details
GROUP BY SNAME, PNAME
ORDER BY SNAME, PNAME
The result set is correct. Although I want to know if there any other optimized way of writing it in SQL. Since both the tables will grow extremely large in size.
Thanks.
I think I would be more inclined to write this as a LEFT JOIN:
SELECT d.SName, d.PName,
SUM(CASE WHEN de.ExSname IS NOT NULL THEN 0 ELSE SoldCount END) AS SoldCount,
SUM(Value) AS Value
FROM #Details d LEFT JOIN
#DetailsException de
ON de.ExSName = d.SName AND de.ExPName = d.PNAme
GROUP BY d.SNAME, d.PNAME
ORDER BY d.SNAME, d.PNAME;
However, your method is fine.
Considering the ExSName, ExPName is unique in
Here is another way using LEFT JOIN.
SELECT SName,
PName,
Sum(CASE
WHEN de.ExSName IS NULL THEN ( SoldCount )
ELSE 0
END) AS SoldCount,
Sum(Value) AS Value
FROM #Details d
LEFT JOIN (select distinct ExSName,ExPName from #DetailsException) de
ON de.ExSName = d.SName
AND de.ExPName = d.PNAme
GROUP BY SNAME,
PNAME
ORDER BY SNAME,
PNAME
Check the performance by running the queries. You can create Non-clustered index on SName,PNAme column to improve the performance
CREATE NONCLUSTERED INDEX IX_details_temp
ON #Details (SNAME, PNAME)
include (SoldCount, Value);
Added covering index (SoldCount, Value) to avoid Clustered/Heap lookup. Note, Index will be useful only if you are going to filter records from #details table. And for #DetailsException table creating the below index could be helpful
CREATE NONCLUSTERED INDEX IX_DetailsException_temp
ON #DetailsException (ExSName, ExPName)

How to group two categories as a one group in SQL

I want to group C,C++,C# and VB,VB.NET as a different single category with summarized result. Do I need to substitute the team name into a unified value (e.g. replace C,C++,C# into "C related") and then do the grouping?
Which one is better in terms of performance? do i need to use Store procedures?
Can someone please advise me how to do it? Thanks!
PS: I am using SQL server
Original table
INSERT INTO Table1
([Team], [Room])
VALUES
('Java', 'Room A'),
('Java', 'Room B'),
('Java', 'Room C'),
('Java', 'Room A'),
('Java', 'Room C'),
('C++', 'Room A'),
('C++', 'Room B'),
('C++', 'Room C'),
('C', 'Room A'),
('C', 'Room B'),
('C', 'Room C'),
('VB', 'Room B'),
('VB', 'Room C'),
('C#', 'Room A'),
('C#', 'Room B'),
('C#', 'Room C'),
('VB.Net', 'Room A'),
('VB.Net', 'Room B'),
('VB.Net', 'Room C')
Expected output
('Team', 'Room', 'Count'),
('Java', 'Room A', 2),
('Java', 'Room B', 1),
('Java', 'Room C', 2),
('C related', 'Room A', 3),
('C related', 'Room B', 3),
('C related', 'Room C', 3),
('VB related', 'Room A', 1),
('VB related', 'Room B', 2),
('VB related', 'Room C', 2)
I would have another table that maps the language to a language family. That is, Java would map to a value of, say, 1, the C-related to 2, and the VB-related to 3. Then, you can join the two tables and group by the language family. This should perform better than grouping by the strings. If you are interested in improving the performance even more, you could denormalize and add a column to your table for the language family.
You can use a group by query with a case statement:
select (case when team in ('C', 'C++', 'C#') then 'C related'
when team in ('VB', 'VB.NET') then 'VB related'
else team
end) as team,
room, count(*)
from table1
group by (case when team in ('C', 'C++', 'C#') then 'C related'
when team in ('VB', 'VB.NET') then 'VB related'
else team
end), room;
If you want to group in SQL itself, then creating a lookup table first
will be faster in general. The number of records, indexing, and frequency
of usage will all affect response times as well.
First create a lookup table for grouping teams:
INSERT INTO Teams ([Team], [TeamGroup])
VALUES
('Java','Java'),
('C++', 'C related'),
('C', 'C related'),
('C#', 'C related'),
('VB', 'VB related'),
('VB.Net', 'VB related')
Then run this query:
SELECT g.TeamGroup AS Team, t.Room, count(g.TeamGroup) AS Count
FROM Team AS t
INNER JOIN Teams AS g ON t.Team=g.Team
GROUP BY g.TeamGroup, t.Room