dynamic grouping by in sql server - sql

i want to make a stored procedure like makeGroupBy(#a int,#b int,#c int). The inputs are 0 or 1 to decide which columns to group by. My attempt so far is below:
-- exec makeGroupBy 0,1,0
-- exec makeGroupBy 0,1,1
create proc makeGroupBy(#product_id int = 0,#city_id int = 1,#date_key
int = 0) as
begin
declare #tbl as table(product_id int, city_id int, date_key int, amount
float)
insert into #tbl
values(1,1,1,10),
(1,1,1,10),
(1,2,1,5),
(2,2,3,15),
(2,1,3,20),
(3,1,1,25)
select case isnull(#product_id,0) when 0 then 0 else product_id end
,case isnull(#city_id,0) when 0 then 0 else city_id end
,case isnull(#date_key,0) when 0 then 0 else date_key end
, sum(amount) amount from #tbl
group by case isnull(#product_id,0) when 0 then 0 else product_id end
,case isnull(#city_id,0) when 0 then 0 else city_id end
,case isnull(#date_key,0) when 0 then 0 else date_key end
end
I don't know if it's possible but what I want is to omit the unwanted columns (inputs with value 0) in the result set.

Assuming that your sql-server version is greater or equal than 2008.
select
product_id
,city_id
,date_key
,sum(amount) as total_amount
from #tbl
group by grouping sets (
(product_id, city_id, date_key)
, (product_id,city_id)
, (product_id, date_key)
, (city_id, date_key)
, (product_id)
, (city_id)
, (date_key))
having concat(iif(grouping_id(product_id)=0,1,0),iif(grouping_id(city_id)=0,1,0),iif(grouping_id(date_key)=0,1,0)) = concat(#product_id, #city_id, #date_key)
order by concat(iif(grouping_id(product_id)=0,1,0),iif(grouping_id(city_id)=0,1,0),iif(grouping_id(date_key)=0,1,0))
It seems that a view will probably suit best for this case
create view [view_name]
as
select
product_id
,city_id
,date_key
,sum(amount) as amount
,concat(iif(grouping_id(product_id)=0,1,0),iif(grouping_id(city_id)=0,1,0),iif(grouping_id(date_key)=0,1,0)) as grp_key
from #tbl
group by grouping sets (
(product_id, city_id, date_key)
, (product_id,city_id)
, (product_id, date_key)
, (city_id, date_key)
, (product_id)
, (city_id)
, (date_key))
go
Then you can query the view like
select
city_id
,date_key
,amount
from [view_name]
where grp_key = concat(0,1,1)

Related

sql query issue: warehouse aging inventory when negative entries

I found a warehouse aging inventory example online (see modified code below).
Everything works fine if the entry type for purchase (0) is positive and for sales (1) is negative. But if the values are inverse (because of cancellation) then the results will be wrong.
Example: There are four entries, three of them are purchase entries but as you can see the second one has been canceled that's why the quantity is negative.
The total sum of column RemainingQty must be 0 in that case but result is 1699.
What do I have to change in my SQL query?
Thanks for any advice.
DECLARE #ItemLedgerEntry TABLE
(
id INT IDENTITY(1, 1) NOT NULL PRIMARY KEY ,
ItemNo INT NOT NULL, --references another item
Qty FLOAT NOT NULL, --quantity
EntryType INT NOT NULL, --type=0 bought, type=1 sold
PostingDate DATETIME NOT NULL -- transaction date
);
INSERT #ItemLedgerEntry
( ItemNo, qty, EntryType, PostingDate )
VALUES ( 1999, 1700, 0, '10-06-2021'),
( 1999, -1700, 0, '29-06-2021'),
( 1999, 1, 0, '03-08-2021'),
( 1999, - 1, 1, '09-08-2021');
WITH Sold
AS ( SELECT IT.[ItemNo] ,
SUM(IT.Qty) AS TotalSoldQty
FROM #ItemLedgerEntry IT
WHERE It.[EntryType] =1
GROUP BY ItemNo
),
Bought
AS ( SELECT IT.* ,
(
SELECT SUM(RS.Qty)
FROM #ItemLedgerEntry RS
WHERE RS.[EntryType] =0 AND RS.[ItemNo] = IT.[ItemNo] AND RS.[PostingDate] <= IT.[PostingDate]
) AS RunningBoughtQty
FROM #ItemLedgerEntry IT
WHERE IT.[EntryType] = 0
)
SELECT
B.[ItemNo],
B.[PostingDate],
B.[EntryType],
S.TotalSoldQty,
B.RunningBoughtQty,
B.RunningBoughtQty + S.TotalSoldQty AS RunningDifferenceQty,
CASE WHEN (B.RunningBoughtQty) + (S.TotalSoldQty) <0
THEN 0
ELSE B.RunningBoughtQty + S.TotalSoldQty
END AS RunningRemainingQty,
CASE WHEN B.RunningBoughtQty + S.TotalSoldQty < 0 THEN 0
WHEN B.RunningBoughtQty + S.TotalSoldQty > B.Qty THEN B.Qty
ELSE B.RunningBoughtQty + S.TotalSoldQty
END AS RemainingQty
FROM Bought B
inner JOIN Sold S ON B.[ItemNo] = S.[ItemNo]

SQL - Split values from one column into two separate columns

I am trying to take the value of column Quantity and split the values where Quantity is less than 500 and put those records into the LessThan500 column, for the records that are greater than 500 into the GreaterThan500 column.
I have a select statement that does this, but the table does not get updated properly
SUM(CASE WHEN Quantity < 500 THEN CAST(( Quantity*10 ) AS INT)
ELSE ''
END) as Quantityx10
, SUM(CASE WHEN Quantity < 500 THEN CAST(( Quantity*10 ) AS INT)
ELSE ''
END) AS '<500'
, '' AS '>=500'
USE FET
-- Create a new template table
IF OBJECT_ID('dbo.FuelSales', 'U') IS NOT NULL
DROP TABLE dbo.FuelSales;
CREATE TABLE FuelSales
(
TransactionType varchar(1) null,
TransactionID int,
CustomerID int null,
TransactionDate date null,
EntryTimeofDay datetime null,
UserTranNumber varchar(15) null,
AircraftNumber varchar(10) null,
CompanyName varchar(40) null,
NumNameCode varchar(30) null,
Description varchar(40) null,
Quantity decimal(18,6) null,
LessThan500 decimal(18, 6) null,
GreaterThan500 decimal(18,6) null,
);
INSERT INTO FuelSales(TransactionType, TransactionID, CustomerID, TransactionDate, EntryTimeofDay,UserTranNumber,AircraftNumber,CompanyName,NumNameCode,Description,Quantity,LessThan500,GreaterThan500)
SELECT ci.TransactionType
, ci.TransactionID
, ci.CustomerID
, ci.TransactionDate
, ci.EntryTimeofDay
, ci.UserTranNumber
, d.AircraftNumber
, c.CompanyName
, d.NumNameCode
, d.Description
, Quantity
FROM [TFBO7].[dbo].[CustInv] ci
INNER JOIN [TFBO7].[dbo].[Cust] c
ON c.CustomerID=ci.CustomerID
INNER JOIN [TFBO7].[dbo].[CustIDet] d
ON ci.TransactionID=d.TransactionID
WHERE ci.TransactionDate between '20180701' and '20180731' and d.TransactionTypeID='1'
I'm not sure why would you do this permanently on the table and not just do the separation as an ad hoc query when needed but assuming it's SQL Server it would go something like this (in the INSERT statement you're already doing):
...
, Quantity
-- 500 has to go somewhere so I put it in the first category
, LessThan500 = CASE WHEN Quantity <= 500 THEN Quantity ELSE NULL END
, GreaterThan500 = CASE WHEN Quantity > 500 THEN Quantity ELSE NULL END
FROM [TFBO7].[dbo].[CustInv] ci
...
You can add a computed column to the table. Just do:
alter table fuelsales add lessthan500 as (case when quantity <= 500 then quantity end);
alter table fuelsales add greaterthan500 as (case when quantity > 500 then quantity end);
This column will appear in the table when you query the table. However, it is not stored and it is "updated" automatically when quantity changes.

SQL query when result is empty

I have a table like this
USER itemnumber datebought (YYYYmmDD)
a 1 20160101
b 2 20160202
c 3 20160903
d 4 20160101
Now I have to show the total number of items bought by each user after date 20160202 (2 february 2016)
I used
SELECT USER, COUNT(itemnumber)<br/>
FROM TABLE<br/>
WHERE datebought >= 20160202<br/>
GROUP BY USER<br>
It gives me results
b 1
c 1
but I want like this
a 0
b 1
c 1
d 0
Please tell me what is the most quick method / efficient method to do that ?
Try like this,
DECLARE #table TABLE
(
[USER] VARCHAR(1),
itemnumber INT,
datebought DATE
)
INSERT INTO #TABLE VALUES
('a',1,'20160101'),
('b',2,'20160202'),
('b',2,'20160202'),
('b',2,'20160202'),
('c',3,'20160903'),
('d',4,'20160101')
SELECT *
FROM #TABLE
SELECT [USER],
Sum(CASE
WHEN datebought >= '20160202' THEN 1
ELSE 0
END) AS ITEMCOUNT
FROM #TABLE
GROUP BY [USER]
Use this
SELECT USER, COUNT(itemnumber)
FROM TABLE
WHERE datebought >= 20160202
GROUP BY USER
Though this query won't be a good idea for the large amount of data:
SELECT USER, COUNT(itemnumber)
FROM TABLE
WHERE datebought >= 20160202
GROUP BY USER
UNION
SELECT DISTINCT USER, 0
FROM TABLE
WHERE datebought < 20160202
USE tempdb
GO
DROP TABLE test1
CREATE TABLE test1(a NVARCHAR(10), ino INT, datebought INT)
INSERT INTO dbo.test1
( a, ino, datebought )
VALUES ( 'a' , 1 , 20160101)
INSERT INTO dbo.test1
( a, ino, datebought )
VALUES ( 'b' , 2 , 20160202)
INSERT INTO dbo.test1
( a, ino, datebought )
VALUES ( 'c' , 3 , 20160903)
INSERT INTO dbo.test1
( a, ino, datebought )
VALUES ( 'd' , 4 , 20160101)
SELECT * FROM dbo.test1
SELECT a, COUNT(ino) OVER(PARTITION BY a) FROM dbo.test1
WHERE datebought>=20160202
UNION ALL
SELECT a, 0 FROM dbo.test1
WHERE datebought<20160202
ORDER BY a

Selection One Entry Only As Non Zero In SQl Select

I have a scenario where I have to select multiple rows from table, I have multiple rows of one record but with different status,
at times I have two identical rows with identical data for status < for that case I canted to select Non zero for the first occurrence and set 0 for the remaining occurrences.
Below is the Image to show and I have marked strike-out and marked 0 for the remaining occurrence.
And body could suggest better SQL Query:
Here is the Query: I am getting zero value for status 1 for ID =1 but I need to show first as regular and then 0 if that status repeats again.
CREATE TABLE #Temp
(ID INT,
ItemName varchar(10),
Price Money,
[Status] INT,
[Date] Datetime)
INSERT INTO #Temp VALUES(1,'ABC',10,1,'2014-08-27')
INSERT INTO #Temp VALUES(1,'ABC',10,2,'2014-08-27')
INSERT INTO #Temp VALUES(1,'ABC',10,1,'2014-08-28')
INSERT INTO #Temp VALUES(2,'DEF',25,1,'2014-08-26')
INSERT INTO #Temp VALUES(2,'DEF',25,3,'2014-08-27')
INSERT INTO #Temp VALUES(2,'DEF',25,1,'2014-08-28')
INSERT INTO #Temp VALUES(3,'GHI',30,1,'2014-08-27')
SELECT CASE WHEN Temp.Status = 1 THEN
0
ELSE
Temp.Price END AS Price,
* FROM (SELECT * FROM #Temp) Temp
DROP TABLE #Temp
Here is the result:
You might modify your inner select using Row_Number() and set price to Zero for RowNumber > 1.
SELECT CASE WHEN Temp.RowNumber > 1 THEN
0
ELSE
Temp.Price END AS Price,
* FROM (
SELECT *,Row_Number() over (PARTITION by ID,Status ORDER BY ID,Date) AS 'RowNumber'
FROM #Temp
) Temp
Order by ID,Date
You can try this:
;WITH DataSource AS
(
SELECT RANK() OVER (PARTITION BY [ID], [ItemName], [Price], [Status] ORDER BY Date) AS [RankID]
,*
FROM #Temp
)
SELECT [ID]
,[ItemName]
,IIF([RankID] = 1, [Price], 0)
,[Status]
,[Date]
FROM DataSource
ORDER BY [ID]
,[Date]
Here is the output:
please try this below code . it is working for me.
CREATE TABLE #Temp
(ID INT,
ItemName varchar(10),
Price Money,
[Status] INT,
[Date] Datetime)
INSERT INTO #Temp VALUES(1,'ABC',10,1,'2014-08-27')
INSERT INTO #Temp VALUES(1,'ABC',10,2,'2014-08-27')
INSERT INTO #Temp VALUES(1,'ABC',10,1,'2014-08-28')
INSERT INTO #Temp VALUES(2,'DEF',25,1,'2014-08-26')
INSERT INTO #Temp VALUES(2,'DEF',25,3,'2014-08-27')
INSERT INTO #Temp VALUES(2,'DEF',25,1,'2014-08-28')
INSERT INTO #Temp VALUES(3,'GHI',30,1,'2014-08-27')
select *,case when a.rn=1 and status!=2 then price else 0 end as price from
(select *,ROW_NUMBER() over(partition by status,date order by date asc) rn from #Temp) a
order by ItemName asc
You can do this with UNION:
SELECT * FROM #Temp t
WHERE NOT EXISTS
(SELECT * FROM #Temp
WHERE t.id = id and t.status = status and t.date < date)
UNION ALL
SELECT ID, ItemName, 0 as Price, status, date
WHERE EXISTS
(SELECT * FROM #Temp
WHERE t.id = id and t.status = status and t.date < date)
Or subquery:
SELECT CASE
WHEN (SELECT COUNT(*)
FROM #Temp
WHERE t.id = id and t.status = status
and t.date > date) > 1 THEN 0 ELSE price END as NewPrice, t.*
FROM #Temp t
Or possibly RANK() function:
SELECT CASE
WHEN RANK() OVER (PARTITION BY id, status ORDER BY date) > 1
THEN 0 ELSE Price END,
t.*
FROM #Temp t

sql query serial number

I have written a stored procedure in SQL Server 2000. I want a serial number for output table.
So when I run this stored proc I get this error:
An explicit value for the identity column in table
'#tmpSearchResults1' can only be specified when a column list is used
and IDENTITY_INSERT is ON.
I have tried with set IDENTITY_INSERT #tmpSearchResults1 on
Create Procedure dbo.usp_mobile_All_KeyWord(#searchkey varchar(30))
AS
CREATE TABLE #tmpSearchResults
(
property_id varchar(255),
property_number varchar(255),
auction_date_reason varchar(255)
)
INSERT INTO #tmpSearchResults
SELECT
p.property_id, p.property_number, p.auction_date_reason
FROM
Pr p
INNER JOIN
Au a ON p.auction_id = a.auction_id
INNER JOIN
PrAdd pa ON p.property_id = pa.property_id
INNER JOIN state AS s ON s.state_id=pa.state
where
(
(p.archive = 'N'
AND
a.show_on_site = 'Y'
AND
(
(
((p.auction_date >= CONVERT(datetime, CONVERT(varchar, GETDATE(), 103), 103) and (p.auction_date_reason is null or p.auction_date_reason = ''))
or
(p.auction_date <= CONVERT(datetime, CONVERT(varchar, GETDATE(), 103), 103) and ( p.auction_date_reason = 'Accepting Offers' )))
and
pa.property_address_type_id = 1 )) )
and
(state_abbreviation=#searchkey or s.state_name like '%'+''+ #searchkey +''+'%' or city like '%'+''+ #searchkey +''+'%' or pa.address1 like '%'+''+ #searchkey +''+'%'
or pa.address2 like '%'+''+ #searchkey +''+'%')
)
)
CREATE TABLE #tmpSearchResults1
(
i1 int identity,
property_id varchar(255),
property_number varchar(255),
auction_date_reason varchar(255)
)
insert into #tmpSearchResults1
select
property_id ,
property_number,
auction_date_reason
from #tmpSearchResults
order by
case when charindex(#searchkey,state) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,statename) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,city) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,address2) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,address1) >0 then 1000 else 0 end desc,
case when charindex(#searchkey,short_description) >0 then 1000 else 0 end desc
select * from #tmpSearchResults1
Plz do help me
The error code is very very very clear.
The relevant portion is ...when a column list is used....
You need to specify your column list in the INSERT statement.
INSERT INTO #tmpSearchResults
(i1,
property_id,
property_number,
auction_date_reason)
SELECT
p.property_id, p.property_number, p.auction_date_reason
FROM...
First, there is a comma too much in the SELECT part of your second statement:
insert into #tmpSearchResults1
select
property_id ,
property_number,
auction_date_reason , <-- THIS ONE!!
from #tmpSearchResults
The last column of a SELECT statement must be without a comma.
So this would be correct:
insert into #tmpSearchResults1
select
property_id ,
property_number,
auction_date_reason
from #tmpSearchResults
Second, did you read this part of the error message?
An explicit value [...] can only be specified when a column list is used
The "column list" part means that you have to specify the columns in the INSERT part:
insert into #tmpSearchResults1
(property_id, property_number, auction_date_reason)
select
property_id ,
property_number,
auction_date_reason
from #tmpSearchResults
You can get away with not specifying the columns when the number of columns in the SELECT statement is the same as in the table in which they should be inserted (and if the data types match).
If one of these conditions is not met, you need to specify the columns because otherwise SQL Server doesn't know which value to insert into which column.