I have a table in SQL Server 2012. The following query works great:
SELECT TOP 300 [ObjectID], [tbh_Objects].Title, [Discount], [tbh_Section].Title
FROM [ECom].[dbo].[tbh_Objects]
INNER JOIN [tbh_Section] ON tbh_Objects.SectionID = tbh_Section.SectionID
ORDER BY tbh_Objects.AddedDate DESC
I want to fire a query which increases the discount value to a random % in the range of 5-10 for all 300 rows at once. So for eg: If DIscount of ObjectID=500 is 30, and the random value between 5 and 10 is "6", I want it to become 30+6%of30 for ObjectID=500.
Similarly for Object ID=230, let's say discount is 20 and the random value is 8, I want it as 20+8%of20.
The end result of the Discount should always be a whole number and not a decimal, so automatically rounds off.
Is this possible in SQL Server? How?
You need random integers and a Modulus (%) operator. A possible approach to generate a random integers is using a combination of NEWID() and CHECKSUM() functions. The following simplified example is a possible solution to your problem:
SELECT
Discount,
RandomPercent,
CONVERT(int, (Discount * (100.0 + RandomPercent) / 100)) AS NewDiscount
FROM (
SELECT Discount, (ABS(CHECKSUM(NEWID()) % 6) + 5) AS RandomPercent
FROM (VALUES (30), (20), (50), (70), (11), (21), (13), (15), (1), (6)) v (Discount)
) t
Result:
Discount RandomPercent NewDiscount
----------------------------------
30 7 32
20 5 21
50 6 53
70 10 77
11 9 11
21 9 22
13 8 14
15 10 16
1 6 1
6 5 6
If you need an UPDATE statement:
;WITH UpdateCTE AS (
SELECT TOP 300 o.[Discount]
FROM [ECom].[dbo].[tbh_Objects] o
INNER JOIN [tbh_Section] s ON o.SectionID = s.SectionID
ORDER BY o.AddedDate DESC
)
UPDATE UpdateCTE
SET [Discount] = CONVERT(int, (o.[Discount] * (100.0 + (ABS(CHECKSUM(NEWID()) % 6) + 5)) / 100))
If you want to round the new discounts before the integer conversion, use ROUND():
SET [Discount] = CONVERT(
int,
ROUND(o.[Discount] * (100.0 + (ABS(CHECKSUM(NEWID()) % 6) + 5)) / 100, 0)
)
Related
I have this little script that shall return the first number in a column of type int which is not used yet.
SELECT t1.plu + 1 AS plu
FROM tovary t1
WHERE NOT EXISTS (SELECT 1 FROM tovary t2 WHERE t2.plu = t1.plu + 1)
AND t1.plu > 0;
this returns the unused numbers like
3
11
22
27
...
The problem is, that when I make a simple select like
SELECT plu
FROM tovary
WHERE plu > 0
ORDER BY plu ASC;
the results are
1
2
10
20
...
Why the first script isn't returning some of free numbers like 4, 5, 6 and so on?
Compiling a formal answer from the comments.
Credit to Larnu:
It seems what the OP really needs here is an (inline) Numbers/Tally (table) which they can then use a NOT EXISTS against their table.
Sample data
create table tovary
(
plu int
);
insert into tovary (plu) values
(1),
(2),
(10),
(20);
Solution
Isolating the tally table in a common table expression First1000 to produce the numbers 1 to 1000. The amount of generated numbers can be scaled up as needed.
with First1000(n) as
(
select row_number() over(order by (select null))
from ( values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0) ) a(n) -- 10^1
cross join ( values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0) ) b(n) -- 10^2
cross join ( values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0) ) c(n) -- 10^3
)
select top 20 f.n as Missing
from First1000 f
where not exists ( select 'x'
from tovary
where plu = f.n);
Using top 20 in the query above to limit the output. This gives:
Missing
-------
3
4
5
6
7
8
9
11
12
13
14
15
16
17
18
19
21
22
23
24
`select
order_price,
To_char(to_date(order_date,'YYYY-MM-DD HH24:MI:SS'),'yyyymmdd hh24') as order_date,
SUM(order_price) OVER(ORDER BY To_char(to_date(order_date,'YYYY-MM-DD HH24:MI:SS'),'yyyymmdd
hh24')) as "bth"
from order_tbl
where seller_no=100
order by order_date;`
I got this result.
But the data I want to get is as follows.
20000 / 20220524 15 / 52500
13000 / 20220524 15 / 52500
19500 / 20220524 15 / 52500
19600 / 20220524 16 / 72100
222000 / 20220524 17 / 738700
and even if there is no data...
0 / 20220524 18 / 738700
0 / 20220524 19 / 738700
0 / 20220524 20 / 738700
.
.
.
.
0 / 20220525 10 / 738700
13600 / 20220525 11 / 787300
like this.
I want to get the order_date and bth for every hour even if there is no data.
It's too difficult for me, but how can I do?
i will remove order_price and add distinct later.
You can try this: (read the guide below)
db<>fiddle
WITH all_hour AS (
SELECT LEVEL AS hour
FROM dual
CONNECT BY LEVEL <= 24
),
all_date AS (
SELECT TO_CHAR(DATE'2022-05-24' + LEVEL - 1, 'YYYYMMDD') AS dt
FROM dual
CONNECT BY LEVEL <= (DATE'2022-05-27' - DATE'2022-05-24' + 1)
),
all_date_hour AS (
SELECT dt || ' ' || (CASE WHEN hour < 10 THEN '0' || TO_CHAR(hour) ELSE TO_CHAR(hour) END) AS order_date
FROM all_date
CROSS JOIN all_hour
),
your_order AS (
SELECT
order_price,
TO_CHAR(TO_DATE(order_date,'YYYY-MM-DD HH24:MI:SS'),'YYYYMMDD HH24') AS order_date,
seller_no
FROM order_tbl
),
your_sum AS (
SELECT adh.order_date, SUM(CASE WHEN yo.seller_no = 100 THEN yo.order_price ELSE 0 END) AS bth
FROM all_date_hour adh
LEFT JOIN your_order yo ON adh.order_date = yo.order_date
GROUP BY adh.order_date
)
SELECT order_date, SUM(bth) OVER(ORDER BY order_date) AS bth
FROM your_sum
ORDER BY order_date;
Summary:
(1) Table 1 : all_hour
includes numbers ranging from 1 to 24
(2) Table 2 : all_date
includes dates from '2022-05-24' to '2022-05-27'.
if your prefer range is '2022-01-01' to '2022-12-31', just simply change '2022-05-24'(Line 7 & 9) -> '2022-01-01', and '2022-05-27'(Line 9) -> '2022-12-31'
(3) Table 3 : all_date_hour
includes dates in the format, 'YYYYMMDD HH24', e.g. '20220524 01'
it is a result from cross joining the first and second table
(4) Table 4: your_order
same as your sample table, order_tbl, just reformatting the order_date in the format, 'YYYYMMDD HH24', e.g. '20220524 01'
(5) Table 5: your_sum (NOT ACCUMULATED YET)
simple summation of order_price, group by the order_date
left join is use here so that all dates from all_date_hour is included
any additional conditions can be added inside the case statement (Line 24)
for example, see (Line 24) SUM(CASE WHEN yo.seller_no = 100 AND youradditionalcondition = somecondition THEN yo.order_price ELSE 0 END)
(6) Final Select Query:
accumulated sum is done here using SUM() OVER(ORDER BY *yourexpr*)
You can use this query - explained later:
select
to_date('01/01/2022 00:00:00','dd/mm/yyyy hh24:mi:ss') + all_hours.sequence_hour/24 order_date_all,
order_price,
To_char(order_date, 'yyyymmdd hh24') as order_date_real,
SUM(order_price) OVER(ORDER BY To_char(order_date, 'yyyymmdd hh24')) as "bth"
from order_tbl ,
(SELECT LEVEL sequence_hour
FROM dual
CONNECT BY LEVEL <= 10) all_hours
where trunc((order_date(+)-to_date('01/01/2022 00:00:00','dd/mm/yyyy hh24:mi:ss'))*24) = all_hours.sequence_hour
and seller_no(+)=100
order by 1;
Explanation:
you don't need to perform To_char(to_date(order_date,'YYYY-MM-DD HH24:MI:SS'),'yyyymmdd hh24') - it means that you take "order_date" which is already date and convert it again to date. It is not needed
I have added maximum number of hours - it is the subquery all_hours. Just for the example it is limited to 10, you can change it to any other value
I have added starting point of time from which you want to display the data "01/01/2022" - change it if you want. Pay attention that it appears in 2 places.
I have added an outer join with order_tbl - pay attention for "(+)" in where conditions. If you want to add additional conditions on order_tbl - remember to add the (+) on the right side of the column as I did with "seller_no"
I have column data. I need to insert ids in another column. Total i have 7 ids. For first 7 values i have to insert these ids and next 7 values, i have to insert same ids and so on.. Can any one please help?
Pay_headID Pay_amount
16414 8000
16415 300
16416 0
16417 200
16418 500
16419 0
16420 0
16414 9000
16415 300
so on ...
you can use CTE and ROW_NUMBER, i have used ordering by Pay_headId:
WITH cte_myTable
AS (SELECT
*,
(ROW_NUMBER() OVER (ORDER BY Pay_headID)) - 1 AS num
FROM myTable)
UPDATE cte_myTable
SET [Pay_headID] =
CASE
WHEN num % 7 = 0 THEN 16414
WHEN num % 7 = 1 THEN 16415
WHEN num % 7 = 2 THEN 16416
WHEN num % 7 = 3 THEN 16417
WHEN num % 7 = 4 THEN 16418
WHEN num % 7 = 5 THEN 16419
WHEN num % 7 = 6 THEN 16420
END
GO
If you want use ordering on how it was inserted, you can set Pay_headIds to null:
update myTable set Pay_headID=null;
You should use RowNum() to give you an artificial incrementing number, divide it by 7 and then Round it.
SELECT FLOOR((ROW_NUMBER() OVER(ORDER BY Pay_HeadID DESC))/7) AS MyID
to get your ids
I have a table in the following format
reporting_date interest_payment balance
200401 10 10
200402 20 15
200403 30 20
200404 40 30
200405 50 40
200406 60 50
200407 70 60
i wanted to generate an OUTPUT in the following format :
The output of the query should look like this :
reporting_date interest_payment balance
Q1 -2004 60 10
Q2 -2004 150 30
Q3 -2004 70 60
Q4 -2004 0 0
i.e i wanted to represent data by quarter and year and group by quarter and year for interest_payment column but for balance i need to pick up the value from the first reporting date in that quarter ,so as you can see q1-2004 has 10,15 and 20 but only 10 is accounted as that was the first reporting date in that quarter
I have my query working for interest payment but i am not sure how do i pickup the first reporting value for balance in a quarter
SELECT report_year as "#date",'Q'+CAST(report_quarter+1 as varchar(1)) as "#quarter", SUM(a.balance) as "#balance", SUM(a.interest_payment) as "#interest_payment"
FROM (SELECT *,
(reporting_date%100 - 1)/3 as report_quarter,
reporting_date/100 as report_year
FROM employee) a
GROUP by report_year, report_quarter
order by report_year, report_quarter
First, create all combination of years and quarters. Then use window functions such AS SUM OVER() and ROW_NUMBER() for the interest_payment and balance.
CREATE TABLE #temp(
reporting_date INT,
interest_payment INT,
balance INT
)
INSERT INTO #temp VALUES
(200401, 10, 10), (200402, 20, 15), (200403, 30, 20),
(200404, 40, 30), (200405, 50, 40), (200406, 60, 50),
(200407, 70, 60);
;WITH quarters(yr, qtr) AS(
SELECT
y.N, q.N
FROM(
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) q(N)
CROSS JOIN(
SELECT DISTINCT reporting_date / 100 FROM #temp
) y(N)
)
,GroupedByQtr AS(
SELECT
*,
qtr = (reporting_date % 100 -1) / 3 + 1,
yr = reporting_date / 100,
ss = SUM(interest_payment) OVER(PARTITION BY (reporting_date / 100), ((reporting_date % 100 -1) / 3 + 1)),
rn = ROW_NUMBER() OVER(PARTITION BY (reporting_date / 100), ((reporting_date % 100 -1) / 3 + 1) ORDER BY reporting_date)
FROM #temp
)
SELECT
reporting_date = 'Q' + CONVERT(VARCHAR(1), q.qtr) + ' - ' + CONVERT(VARCHAR(4), q.yr),
interest_payment = ISNULL(g.ss, 0),
balance = ISNULL(g.balance, 0)
FROM quarters q
LEFT JOIN GroupedByQtr g
ON g.qtr = q.qtr
AND g.yr = q.yr
AND g.rn = 1
RESULT
reporting_date interest_payment balance
-------------- ---------------- -----------
Q1 - 2004 60 10
Q2 - 2004 150 30
Q3 - 2004 70 60
Q4 - 2004 0 0
I've tried to figure out how this SQL query generates a sequence of numbers, and I still don't have a clue.
Digits Table
digit
--------
0
1
2
3
4
5
6
7
8
9
SELECT D3.digit * 100 + D2.digit * 10 + D1.digit + 1 AS n
FROM dbo.Digits as D1
CROSS JOIN dbo.Digits as D2
CROSS JOIN dbo.Digits AS D3
ORDERY BY n;
The Query Result...
n
------
1
2
3
4
5
...
998
999
1000
How does it work?
If you are into CTE, this will give you 1 to 1000.
;
with
Num(Pos) as
(
select cast(1 as int)
union all
select cast(Pos + 1 as int) from Num where Pos < 1000
)
select * from Num option (maxrecursion 0)
A cross join is a Cartesian product: that is, every row joins with every other row.
So a 11 row table joined to a 7 row table gives 77 rows
In your case, you have 10 rows * 10 rows * 10 rows = 1000.
Try this query to see the raw date before you generate the number
SELECT D3.digit, D2.digit, D1.digit
FROM dbo.Digits as D1
CROSS JOIN dbo.Digits as D2
CROSS JOIN dbo.Digits AS D3
ORDER BY d3, d2, d1;
The way you have 100*d3 + 10*d2 + d1 replicates how we count naturally and carry in addition.
CROSS JOIN is much like an INNER JOIN MYTable on 1 = 1, resulting in the Cartesian Product of your Input Sets
Basically, for each record on the left, it joins for each record on the right.
In the case of a 10-digit source table, the first cross join results in 100 records.
In the case of a second cross join to the same 10-digit source table, you get all 100 previous records again, for each record in the source table, resulting in 1000 records.
Your resulting table would look like this, if you your Select Statement was "Select * ..." Order by ...
D1 D2 D3
1 2 3
1 2 4
1 2 5
If you take those values in the table above and concatenate them (then add one) you get consecutive numbers.
"1" + "2" + "3" = 123 (+1 = 124)
"1" + "2" + "4" = 124 (+1 = 125)
"1" + "2" + "5" = 125 (+1 = 126)
Obviously, the author is not concatenating. However, he's doing the mathematical equivalent.
1 * 100 + 2 * 10 + 3 * 1 + 1 = 124
1 * 100 + 2 * 10 + 4 * 1 + 1 = 125
1 * 100 + 2 * 10 + 5 * 1 + 1 = 126
Ultimately, the author devised a strange way to provide a listing of numbers from 1 to 1000.
The values of the digit from the D3 table will range from 0 - 900 (D3.digit * 100)
The values of the digit from the D2 table will range from 0 - 90 (D2.digit * 10)
The values of the digit from the D1 table will range from 0 - 9 (D1.digit * 100)
Add them up and you have a range from 0 - 999
Add 1 to the result and you have a range from 1 - 1000