Counting the records in sql server 2012 - sql-server-2012

In my table i want to find out if the productid exists more then once in the table.
Actually textcode is passing as parameter in where condition.
create table productdetails(productid nvarchar(22),textcode varchar(50),textmessage varchar(50))
INSERT INTO productdetails VALUES('T0001','M0001','ONE')
INSERT INTO productdetails VALUES('T0001','M0002','TWO')
INSERT INTO productdetails VALUES('T0009','M0006','THREE')
INSERT INTO productdetails VALUES('T0002','M0001','FIVE')
if i give textcode='M0001' in where condition it gives productid='T0001' AND 'T0002' i want to check whether this productid 'T0001' AND 'T0002' exists more then once in the table.
I used below query.Is it optimized query?or any other method to check this condition.
SELECT COUNT(PRODUCTID),PRODUCTID FROM productdetails WHERE productid in(SELECT PRODUCTID FROM productdetails WHERE TEXTCODE='M0001')
GROUP BY PRODUCTID HAVING COUNT(*)>1

You can use conditional aggregation in the having clause to simplify your query:
SELECT COUNT(*), PRODUCTID
FROM productdetails
GROUP BY PRODUCTID
HAVING COUNT(CASE WHEN TEXTCODE='M0001' THEN 1 END) > 0
AND COUNT(*) > 1
This query should give you better performance since you are only accessing the table once.
Please note that for best performance both queries can probably benefit from the same index - an index on TextCode and ProductId (in that order!) should contribute to both queries performance.

Related

need an additional column in SQL output

I have a table called product_info where there are two columns(product, product_id).There are 5 'product_id' and 10 'product'. I wrote the following code to list products and their count.Now I need to create an additional column called 'favorite_product' if the 'product' count is more than 3.When I tried with a couple of WHERE clause options, it filtered out my existing column (product_count) which I need to keep intact in the output. How can I do that?
Any help would be appreciated.
SELECT product AS Product_Name,
COUNT (product) AS Product_Count
FROM product_info
GROUP BY product
Just use a case expression:
SELECT product AS Product_Name, COUNT(*) AS Product_Count,
(CASE WHEN COUNT(*) > 3 THEN 1 ELSE 0 END) as is_favorite_flag
FROM product_info
GROUP BY product;

How can I improve the native query for a table with 7 millions rows?

I have the below view(table) in my database(SQL SERVER).
I want to retrieve 2 things from this table.
The object which has the latest booking date for each Product number.
It will return the objects = {0001, 2, 2019-06-06 10:39:58} and {0003, 2, 2019-06-07 12:39:58}.
If all the step number has no booking date for a Product number, it wil return the object with Step number = 1. It will return the object = {0002, 1, NULL}.
The view has 7.000.000 rows. I must do it by using native query.
The first query that retrieves the product with the latest booking date:
SELECT DISTINCT *
FROM TABLE t
WHERE t.BOOKING_DATE = (SELECT max(tbl.BOOKING_DATE) FROM TABLE tbl WHERE t.PRODUCT_NUMBER = tbl.PRODUCT_NUMBER)
The second query that retrieves the product with booking date NULL and Step number = 1;
SELECT DISTINCT *
FROM TABLE t
WHERE (SELECT max(tbl.BOOKING_DATE) FROM TABLE tbl WHERE t.PRODUCT_NUMBER = tbl.PRODUCT_NUMBER) IS NULL AND t.STEP_NUMBER = 1
I tried using a single query, but it takes too long.
For now I use 2 query for getting this information but for the future I need to improve this. Do you have an alternative? I also can not use stored procedure, function inside SQL SERVER. I must do it with native query from Java.
Try this,
Declare #p table(pumber int,step int,bookdate datetime)
insert into #p values
(1,1,'2019-01-01'),(1,2,'2019-01-02'),(1,3,'2019-01-03')
,(2,1,null),(2,2,null),(2,3,null)
,(3,1,null),(3,2,null),(3,3,'2019-01-03')
;With CTE as
(
select pumber,max(bookdate)bookdate
from #p p1
where bookdate is not null
group by pumber
)
select p.* from #p p
where exists(select 1 from CTE c
where p.pumber=c.pumber and p.bookdate=c.bookdate)
union all
select p1.* from #p p1
where p1.bookdate is null and step=1
and not exists(select 1 from CTE c
where p1.pumber=c.pumber)
If performance is main concern then 1 or 2 query do not matter,finally performance matter.
Create NonClustered index ix_Product on Product (ProductNumber,BookingDate,Stepnumber)
Go
If more than 90% of data are where BookingDate is not null or where BookingDate is null then you can create Filtered Index on it.
Create NonClustered index ix_Product on Product (ProductNumber,BookingDate,Stepnumber)
where BookingDate is not null
Go
Try row_number() with a proper ordering. Null values are treated as the lowest possible values by sql-server ORDER BY.
SELECT TOP(1) WITH TIES *
FROM myTable t
ORDER BY row_number() over(partition by PRODUCT_NUMBER order by BOOKING_DATE DESC, STEP_NUMBER);
Pay attention to sql-server adviced indexes to get good performance.
Possibly the most efficient method is a correlated subquery:
select t.*
from t
where t.step_number = (select top (1) t2.step_number
from t t2
where t2.product_number = t.product_number and
order by t2.booking_date desc, t2.step_number
);
In particular, this can take advantage of an index on (product_number, booking_date desc, step_number).

Dynamic column in Oracle SQL select

I have three tables:
T_ORDER_PLACEMENTS (ORDER_ID, CUSTOMER_ID, ORDER_DATE)
T_ORDER_DETAILS (ORDER_ID, STOCK_ID)
T_STOCK_DETAILS(STOCK_ID, STOCK_NAME, STOCK_PRICE)
Can someone please help me to write a query which generates the following output:
STOCK_ID, STOCK_NAME, STOCK_PRICE, ORDERED_STATUS
1 stock1 5000 ordered
2 stock2 10000 unordered
Populate the ORDERED_STATUS column with 'ordered' if the stock is ordered and 'unordered' if the stock is unordered.
SELECT
t_stock_details.*,
CASE WHEN order_check.stock_id IS NULL THEN 'unordered' ELSE 'ordered' END AS ordered_status
FROM
t_stock_details
LEFT JOIN
(
SELECT stock_id FROM t_order_details GROUP BY stock_id
)
order_check
ON order_check.stock_id = t_stock_details.stock_id
The sub-query checks to see which stock_ids have an order associated with them. It also uses GROUP BY to ensure only one row is returned per stock_id, no matter how many orders are found.
The LEFT JOIN ensures that every row in t_stock_details is returned, whether or not it is successfully joined to anything. Where there is a successful join, we know there has been an order. It will also only ever be joined on to one row at the most (thanks to the above mentioned GROUP BY, so no duplication is being caused).
An unsuccessful join will have NULL in the order_check.stock_id, so we use that to check which string to return, using a CASE statement.

Update With duplicate key

I have one temp table a with idproduct and Qty like
idproduct Qty
123 2
123 2
And anoth table b like
idproduct stock
123 10
Then i want to update (b.stock-a.qty)
So i want result like
idproduct stock
123 6
But it give me reult like
**idproduct stock
123 8**
From comment:
UPDATE INV
SET INV.stock = isnull(INV.stock,0) - ISNULL(TEMP.QTY,0)
FROM INVENTORY INV, #TempBagPack TEMP
WHERE INV.idproduct = TEMP.idproduct
The problem in your query is clearly mentioned by #Gordon Linoff.
This is just another way of scripting using CTE.
;with cte_1
As
(select idproduct, sum(qty) as qty
from #TempBagPack t
group by idproduct
)
update i
set i.stock = coalesce(i.stock, 0) - coalesce(t.qty, 0)
from Cte_1 t
Join inventory i
on i.idproduct = t.idproduct;
First. Never use commas in the FROM clause. Always use explicit JOIN syntax.
Second, your problem is that multiple rows match for the update, but SQL Server only updates the row once. This is well documented.
The solution is to pre-aggregate the data:
update i
set i.stock = coalesce(i.stock, 0) - coalesce(t.qty, 0)
from inventory i join
(select idproduct, sum(qty) as qty
from #TempBagPack t
group by idproduct
) t
on i.idproduct = t.idproduct;
The warning in the documentation is:
Use caution when specifying the FROM clause to provide the criteria
for the update operation. The results of an UPDATE statement are
undefined if the statement includes a FROM clause that is not
specified in such a way that only one value is available for each
column occurrence that is updated, that is if the UPDATE statement is
not deterministic.

removing non-matching rows based on order in SQL within a WITH statement

I've got a many-to-many setup where there are items and item names(based on languageID)
I want to retrieve all names for a set id, where the name is replace with an alternate name (same itemID, but different languageID) when name is NULL.
I've set up a table that receives all combinations of itemids and itemnames, even the missing ones, and have the name ordered by an hasName flag, that is set based on name existing to 0,1 or 2. 0 means languageId and name exist, 1 means only name exists, and 2 means neither. I then sort the results: ORDER BY itemId, hasName, languageId this works well enough, because the top 1 row of every itemid meats the critera, and I can just pull that.
However I still need to process other queries using the result, so this doesn't work well, because as soon as I use a WITH statement, the order cannot be used, so it breaks the functionality
What I'm using instead is a join, where I select the top 1 matching row on the ordered table
the problem there is that the time to execute goes up 10x
any ideas what else I could try?
using SQL server 10.50
the slow query:
SELECT
*,
(SELECT top 1 ItemName FROM ItemNameMultiLang x WHERE x.ItemId = tc.ItemId ORDER BY ItemID, hasName, LangID) AS ItemName
FROM ItemCategories tc
ORDER BY ItemId
One way to approach this is with row_number(), so you can get the first row from itemNameMultiLang, which is what you want:
SELECT tc.*, inml.ItemName
FROM ItemCategories tc left outer join
(select inml.*, row_number() over (partition by inml.ItemId order by hasname, langId) as seqnum
from ItemNameMultiLang
) inml
on tc.ItemItem = inml.ItemId and
inml.seqnum = 1
ORDER BY tc.ItemId;