How to Insert varchar values into table using loop - sql-server-2008

I got a table called ATMONTH where i need to check the count of customers for each month
Table structure is like
CREATE TABLE [dbo].[ATMONTH](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[Month] [varchar](50) NULL,
[COUNT OF CUSTOMER] [varchar] (50) NULL,
[RefMonthStart] [varchar](50) NULL,
[RefMonthEnd] [varchar](50) NULL)
i'm accepting a output like
1 1month 3000 0 30
2 2Month 4500 31 60
3 3month 4000 61 90
4 4Month 6000 91 120
.
.
.
24 24Month .. .. ..
25 >24Month .. .. ..
where count of customer i'm refering to other table.. here only month, refmonthstart and
refmonthend column must be manually inserted where refmonthstart and refmonthend columns
are number of days in in a month
how will i do it...??
Thanks in Advance

Try the following query
DML:
CREATE TABLE [dbo].[ATMONTH](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[Month] [varchar](50) NULL,
[COUNT OF CUSTOMER] [varchar] (50) NULL,
[RefMonthStart] [varchar](50) NULL,
[RefMonthEnd] [varchar](50) NULL)
INSERT INTO [dbo].[ATMONTH]([Month])
VALUES ('Jan'),
('Feb'),
('Mar'),
('Apr'),
('May'),
('Jun'),
('Jul'),
('Aug'),
('Sep'),
('Oct'),
('Nov'),
('Dec');
CREATE TABLE dbo.MonthsAndDays
(
[Month] VARCHAR(3),
Days SMALLINT
)
INSERT INTO dbo.MonthsAndDays([Month],Days)
VALUES ('Jan',31),
('Feb',28),
('Mar',31),
('Apr',30),
('May',31),
('Jun',30),
('Jul',31),
('Aug',31),
('Sep',31),
('Oct',30),
('Nov',30),
('Dec',31);
Query:
DECLARE #refmonthstart INT = 0
;WITH CTE AS
(
SELECT a.ID,
a.Month,
a.[COUNT OF CUSTOMER],
a.[RefMonthStart],
a.[RefMonthEnd],
b.Days Days,
ROW_NUMBER() OVER ( ORDER BY a.[ID] ASC) i
FROM [dbo].[ATMONTH] a
INNER JOIN dbo.MonthsAndDays b
ON a.[Month] = b.[Month]
)
,
ResultSet AS
(
SELECT ID,
Month,
[COUNT OF CUSTOMER],
#refmonthstart AS RefMonthStart,
Days + #refmonthstart AS RefMonthEnd,
i
FROM CTE
WHERE i = 1
UNION ALL
SELECT T.ID,
T.Month,
T.[COUNT OF CUSTOMER],
R.RefMonthEnd + 1,
T.Days + R.RefMonthEnd,
T.i
FROM ResultSet R
INNER JOIN CTE T
ON R.i + 1 = T.i
)
SELECT *
FROM ResultSet
OPTION (MAXRECURSION 1000)
Query will recursively calculate refmonthstart and refmonthend value. Set the MAXRECURSION value depending upon the number of rows you have in the table.
You can set the initial refmonthstart value where #refmonthstart is declared, I have set it to 0(zero)

Related

How to Duplicate Column in same table in SQL?

i am trying to duplicate a column hotelid , and then i want to make a couples that begin with the same letter,
select cast (hotelid as int) as hotelid1 , cast (hotelid as int) as hotelid2
from hotel
where (postcode BETWEEN 1500 ANd 1999) or
( postcode BETWEEN 3000 and 3499)
and (hotelid2 > hotelid1 )
and ( LEFT(hotelid1, 1) like LEFT(hotelid2, 1))
how i can use the new lines in where conditions ?
MySQL uses HAVING for that
SELECT cast(hotelid as UNSIGNED ) as hotelid1 , cast(hotelid as UNSIGNED ) as hotelid2
from hotel
where (postcode BETWEEN 1500 ANd 1999) or
( postcode BETWEEN 3000 and 3499)
HAVING (hotelid2 > hotelid1 )
and ( LEFT(hotelid1, 1) like LEFT(hotelid2, 1))

Change db schema or query to return balance for a given period of time

I have came up with the following schema:
CREATE TABLE products
(
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
quantity INT(10) UNSIGNED NOT NULL,
purchase_price DECIMAL(8,2) NOT NULL,
sell_price DECIMAL(8,2) NOT NULL,
provider VARCHAR(255) NULL,
created_at TIMESTAMP NULL,
PRIMARY KEY (id)
);
# payment methods = {
# "0": "CASH",
# "1": "CREDIT CARD",
# ...
# }
CREATE TABLE orders
(
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
product_id INT(10) UNSIGNED NOT NULL,
quantity INT(10) UNSIGNED NOT NULL,
payment_method INT(11) NOT NULL DEFAULT 0,
created_at TIMESTAMP NULL,
PRIMARY KEY (id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
# status = {
# "0": "PENDING"
# "1": "PAID"
# }
CREATE TABLE invoices
(
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
price INT(10) UNSIGNED NOT NULL,
status INT(10) UNSIGNED NOT NULL DEFAULT 0,
created_at TIMESTAMP NULL,
PRIMARY KEY (id)
);
# payment methods = {
# "0": 'CASH',
# "1": 'CREDIT CARD',
# ...
# }
CREATE TABLE bills
(
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
name VARCHAR(255) NOT NULL,
payment_method INT(10) UNSIGNED NOT NULL DEFAULT 0,
price DECIMAL(8,2) NOT NULL,
created_at TIMESTAMP NULL,
PRIMARY KEY (id)
);
And the following query to select a balance:
SELECT ((orders + invoices) - bills) as balance
FROM
(
SELECT SUM(p.sell_price * o.quantity) as orders
FROM orders o
JOIN products p
ON o.product_id = p.id
) orders,
(
SELECT SUM(price) as invoices
FROM invoices
WHERE status = 1
) invoices,
(
SELECT SUM(price) as bills
FROM bills
) bills;
Its working and returning the right balance, but I want to create a chart using Morris.js and I need to change it to return a daily or monthly balance at a given period of time and in this format:
Daily (2017-02-27 to 2017-03-01)
balance | created_at
--------------------------
600.00 | 2017-03-01
50.00 | 2017-02-28
450.00 | 2017-02-27
And monthly (2017-01 to 2017-03)
balance | created_at
--------------------------
200.00 | 2017-03
250.00 | 2017-02
350.00 | 2017-01
What I need to change in my schema or query to return results in this way?
http://sqlfiddle.com/#!9/2289a9/2
Any hints are welcomed. Thanks in advance
Include the created_at date in the SELECT list and a GROUP BY clause in each query.
Ditch the old school comma operator for the join operation, and replace it with a LEFT JOIN.
To return dates for which there are no orders (or no payments, or no invoices) we need a separate row source that is guaranteed to return the date values. As an example, we could use an inline view:
SELECT d.created_dt
FROM ( SELECT '2017-02-27' + INTERVAL 0 DAY AS created_dt
UNION ALL SELECT '2017-02-28'
UNION ALL SELECT '2017-03-01'
) d
ORDER BY d.created_dt
The inline view is just an option. If we had a calendar table that contains rows for the three dates we're interested in, we could make use of that instead. What's important is that we have a query that is guaranteed to return to us exactly three rows with the distinct created_at date values we want to return.
Once we have that, we can add a LEFT JOIN to get the value of "bills" for that date.
SELECT d.created_dt
, b.bills
FROM ( SELECT '2017-02-27' + INTERVAL 0 DAY AS created_dt
UNION ALL SELECT '2017-02-28'
UNION ALL SELECT '2017-03-01'
) d
LEFT
JOIN ( SELECT DATE(bills.created_at) AS created_dt
, SUM(bills.price) AS bills
FROM bills
WHERE bills.created_at >= '2017-02-27'
AND bills.created_at < '2017-03-01' + INTERVAL 1 DAY
GROUP BY DATE(bills.created_at)
) b
ON b.created_dt = d.created_dt
ORDER BY d.created_dt
Extending that to add another LEFT JOIN, to get invoices
SELECT d.created_dt
, i.invoices
, b.bills
FROM ( SELECT '2017-02-27' + INTERVAL 0 DAY AS created_dt
UNION ALL SELECT '2017-02-28'
UNION ALL SELECT '2017-03-01'
) d
LEFT
JOIN ( SELECT DATE(bills.created_at) AS created_dt
, SUM(bills.price) AS bills
FROM bills
WHERE bills.created_at >= '2017-02-27'
AND bills.created_at < '2017-03-01' + INTERVAL 1 DAY
GROUP BY DATE(bills.created_at)
) b
ON b.created_dt = d.created_dt
LEFT
JOIN ( SELECT DATE(invoices.created_at) AS created_dt
, SUM(invoices.price) AS invoices
FROM invoices
WHERE invoices.status = 1
AND invoices.created_at >= '2017-02-27'
AND invoices.created_at < '2017-03-01' + INTERVAL 1 DAY
GROUP BY DATE(invoices.created_at)
) i
ON i.created_dt = d.created_dt
ORDER BY d.created_dt
Similarly, we can a LEFT JOIN to another inline view that returns total orders grouped by DATE(created_at).
It's important that the inline views return distinct value of created_dt, a single row for each date value.
Note that for dev, test and debugging, we can independently execute just the inline view queries.
When a matching row is not returned from a LEFT JOIN, for example no matching row returned from i because there were no invoices on that date, the query is going to return a NULL for the expression i.invoices. To replace the NULL with a zero, we can use the IFNULL function, or the more ANSI standard COALESCE function. For example:
SELECT d.created_dt
, IFNULL(i.invoices,0) AS invoices
, COALESCE(b.bills,0) AS bills
FROM ...
To get the results monthly, we'd need a calendar query that returns one row per month. Let's assume we're going to return a DATE value which as the first day of the month. For example:
SELECT d.created_month
FROM ( SELECT '2017-02-01' + INTERVAL 0 DAY AS created_month
UNION ALL SELECT '2017-03-01'
) d
ORDER BY d.created_month
The inline view queries will need to GROUP BY created_month, so they return a single value for each month value. My preference would be to use a DATE_FORMAT function to return the first day of the month, derived from created_at. But there are other ways to do it. The goal is return a single row for '2017-02-01' and a single row for '2017-03-01'. Note that the date ranges on created_at extend from '2017-02-01' up to (but not including) '2017-04-01', so we get the total for the whole month.
( SELECT DATE_FORMAT(bills.created_at,'%Y-%m-01') AS created_month
, SUM(bills.price) AS bills
FROM bills
WHERE bills.created_at >= '2017-02-01'
AND bills.created_at < '2017-03-01' + INTERVAL 1 MONTH
GROUP BY DATE_FORMAT(bills.created_at,'%Y-%m-01')
) b

Combine temporary table of dates with two tables and fill in missing values?

I have a rates table which holds rows of nightly rates per day. I have a ratecodes table which houses different ratecodes mapped to rates.
My goal is to find any missing rates for any days for an X period of time. For this example let's use 1 month.
Desired result: 64 rows of which 2 rows are filled with information with the first rate code. The second rate code has absolutely no rows in rates but I need to show that it's actually missing dates. ( 64 because 1 month from now returns 32 days x 2 rate codes )
Two tables in question:
CREATE TABLE `ratecode` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ratecode` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `ratecode` VALUES ('1', 'BLAH');
INSERT INTO `ratecode` VALUES ('2', 'NAH');
CREATE TABLE `rates` (
`thedate` date DEFAULT NULL,
`rate` double DEFAULT NULL,
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ratecode` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `rates` VALUES ('2014-12-27', '999', '1', '1');
INSERT INTO `rates` VALUES ('2014-12-26', '99', '2', '1');
So using this query, in 2 parts. Part 1 is a temporary table of dates from today to 1 month ahead:
CREATE TEMPORARY TABLE IF NOT EXISTS `myDates` AS (
SELECT
CAST((SYSDATE()+INTERVAL (H+T+U) DAY) AS date) d
FROM ( SELECT 0 H
UNION ALL SELECT 100 UNION ALL SELECT 200 UNION ALL SELECT 300
) H CROSS JOIN ( SELECT 0 T
UNION ALL SELECT 10 UNION ALL SELECT 20 UNION ALL SELECT 30
UNION ALL SELECT 40 UNION ALL SELECT 50 UNION ALL SELECT 60
UNION ALL SELECT 70 UNION ALL SELECT 80 UNION ALL SELECT 90
) T CROSS JOIN ( SELECT 0 U
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) U
WHERE
(SYSDATE()+INTERVAL (H+T+U) DAY) <= (SYSDATE()+INTERVAL 1 MONTH)
ORDER BY d ASC
);
And part 2 is the actual selection going on:
SELECT
*
FROM
rates
RIGHT JOIN myDates ON ( myDates.d = rates.thedate )
LEFT OUTER JOIN ratecode ON ( rates.ratecode = ratecode.id )
This returns only 32 rows back because in rates, there are 2 records for the first entry in ratecode. I don't get back the 32 missing rows for the other ratecode. How can I adjust in order to retain this information?
After I get the 64 rows back, I also need to filter for which ones are "blank" or haven't been entered in rates. So missing values only.
If I understand correctly, you want to generate all the rows using a cross join, then left join to the data and filter out all th ematches:
select rc.ratecode, d.d as missingdate
from ratecode rc cross join
mydates d left join
rates r
on rc.id = r.ratecode and d.d = r.thedate
where r.id is null;

sub query in sql server

I have two tables :
create table sales (
unitcode int ,
categorycode smallint ,
ddate varchar(10) ,
price float
)
and
create table timetable (
year varchar(4) ,
month varchar(11) ,
ddate varchar(10)
)
I want to write a subquery to find :
in each month in each year which 2 products(unitcode , categorycode) have a top of price ?
Try this
;WITH cte AS (
SELECT unitcode,categorycode,t.ddate,price,ROW_NUMBER() OVER (PARTITION BY t.[year],t.[month] ORDER BY price desc) AS price_order,t.[year],t.[month]
FROM sales s
INNER JOIN timetable t
ON t.ddate = s.ddate
)
SELECT *
FROM cte
WHERE price_order <= 2
ORDER BY [year] ASC,[month] ASC,price DESC

Update based on sequence/rank in SQL Server 2008?

Table looks like this:
create table #rankme (rankmeid int identity(1000, 1) primary key,
step int null, checkvalue int null)
insert into #rankme values ( 10 , 1 )
insert into #rankme values ( 15 , null )
insert into #rankme values ( 20 , null )
insert into #rankme values ( 40 , null )
select * from #rankme order by step,checkvalue
Taking step as a parameter, I am trying to find out if the requested checkvalue for the one before the step I asked for is null.
So I want to select where step=20 and get NULL.
And I want to select where step=15 and get a 1.
I was trying to come up with something based on "rank-1" but so far no cigar.
Help?
declare #step int = 15
select top(1) R.checkvalue
from #rankme as R
where R.step < #step
order by R.step desc