SQL query select - sql-server

I am using MSSQL Server and Nodejs with npm mssql. I need to build a query.
Explanation:
Need to select Users from table that did not receive emails today or never receive.
Query example:
SELECT * FROM dbo.Users u
LEFT JOIN dbo.userEmailHistory ueh
ON u.id = ueh.userId
WHERE IsNull(sDate, '') = ''
OR CONVERT(date,sDate)>=CONVERT(date,DATEADD(day,1,getdate()))
Then:
Need to select the user favorite brand. I am doing async. So the code is looking like this:
function getUserSalesByMalls(params, callback) {
var usersArray = [];
var targetID = params.type;
async.eachLimit(params, 1, function (user, cb) {
sqlRequest("
SELECT TOP 1 bts.BrandCategoryID as title, b.title as brand, b.id
FROM dbo.Users st JOIN SaleView ss ON (st.ID=ss.userID)
JOIN Sales sa ON (sa.ID=ss.SaleID)
JOIN Brands b ON (b.ID=sa.BrandID)
JOIN KEY_BrandcategoryToSale bts ON (bts.SaleID=sa.ID)
WHERE st.ID="+**user.id**+"
GROUP BY bts.BrandCategoryID, b.title, b.id
ORDER BY COUNT(bts.BrandCategoryID) desc",
function (err, result) {
user.favBrand = result;
console.log(user);
usersArray.push(user);
cb();
})
}, function () {
callback(null, usersArray)
})
}
Then:
I need to select top 2 sales by user favorite brand that were most viewed in period 14 days and user never seen it:
function getUserSalesByMalls(params, callback) {
var usersArray = [];
var targetID = params.type;
async.eachLimit(params, 1, function (user, cb) {
sqlRequest("
`SELECT DISTINCT TOP 2 s.id as saleId, s.title, s.description, s.imageUrl, count(sv.saleId) as mostViewsPeriod14Days, s.guid, stm.mallId as mallId, brand.title as brandTitle FROM dbo.Sales s
INNER JOIN dbo.SalesToMall stm ON s.id = stm.saleId
INNER JOIN dbo.SaleView sv ON s.id = sv.saleId
INNER JOIN dbo.Brands brand ON s.brandID = brand.id
WHERE (sv.date <= GETDATE())
AND (sv.date >= GETDATE() - 14) AND s.isActive = 1 AND s.isHotSale = 1 AND b.id = "+user.favBrandId+"
AND s.id NOT IN (SELECT DISTINCT sv2.saleId FROM dbo.SaleView sv2
WHERE sv2.userId = "+user.id+")
GROUP BY s.id, s.title, s.description, s.imageUrl, s.guid, stm.mallId, brand.title
ORDER BY COUNT(sv.saleId) DESC`",
function (err, result) {
user.salesByBrand = result;
console.log(user);
usersArray.push(user);
cb();
})
}, function () {
callback(null, usersArray)
})
}
And then last one: i need to select 3 suggestions for the user by mall id that were most viewed in period 14 days and user never seen it:
function getUserSalesByMalls(params, callback) {
var usersArray = [];
var targetID = params.type;
async.eachLimit(params, requestPerLimit, function (user, cb) {
setTimeout(function () {
sqlRequest("SELECT DISTINCT TOP 3 s.id as saleId, s.title, s.description, s.imageUrl, count(sv.saleId) as mostViewsPeriod14Days, s.guid, stm.mallId as mallId, brand.title as brandTitle
FROM dbo.Sales s
INNER JOIN dbo.SalesToMall stm ON s.id = stm.saleId
INNER JOIN dbo.SaleView sv ON s.id = sv.saleId
INNER JOIN dbo.Brands brand ON s.brandID = brand.id
WHERE (sv.date <= GETDATE())
AND (sv.date >= GETDATE() - 14)
AND s.isActive = 1 AND s.isHotSale = 1
AND stm.mallId = "+user.mallId+"
AND s.id NOT IN (SELECT DISTINCT sv2.saleId
FROM dbo.SaleView sv2
WHERE sv2.userId = "+user.id+")
GROUP BY s.id, s.title, s.description, s.imageUrl, s.guid, stm.mallId, brand.title
ORDER BY COUNT(sv.saleId) DESC", function (err, result) {
if (!result) {
cb();
} else if (result.length == 3) {
sqlInsert("INSERT INTO UserEmailHistory (userID,sDate,targetID) VALUES (" + user.id + ",CONVERT(datetime,DATEADD(hour," + utc_diff + ",getdate()))," + targetID + ")");
console.log("USER WITH 3 SALES BY MALL");
user.sales = result;
console.log(user);
usersArray.push(user);
cb();
}
})
}, requestPerMillisecond)
}, function () {
callback(null, usersArray)
})
}
My question is:
Is there any way to combine and build only one query and get all necessary data regarding my explanation?

Related

Convert SQL query to Linq2db

Some one help me to convert SQL query to linq2db join query.
Below is my query.
var query = from a in _accountRepository.Table
join b in _documentRepository.Table on a.ID equals b.AccountID
join c in _documentTypeRepository.Table on b.DocumentTypeID equals c.ID
where a.CompanyID == companyID && b.CompanyID == companyID
&& c.CompanyID == companyID
&& a.IsActive && !a.IsDeleted && c.IsInvoice
&& b.IsActive && !b.IsDeleted
&& b.Date.Date >= fromDate.Date && b.Date.Date <=
toDate.Date
&& (b.AccountID == accountID || accountID == null)
&& (costcenterID.Contains(b.CostCenterID) || costcenterID == null)
&& a.AccountTypeID == (int)DefaultAccountTypes.Customer
group b by new { a.DisplayName, a.ID } into g
select new SalesByCustomerModel
{
AccountID = g.Key.ID,
DisplayName = g.Key.DisplayName,
InvoiceCount = g.Count(),
Sales = g.Sum(x => (x.SubTotal - x.Discount)),
SalesWithTax = g.Sum(x => x.Total),
Tax = g.Sum(x => x.Tax)
};
I have to add this. How can I achive with linq2db.
INNER JOIN ( SELECT ROW_NUMBER() OVER(PARTITION by DocumentID ORDER BY DocumentID) AS SrNo,
DocumentID
FROM DocumentDetail
WHERE (DocumentItemID = #itemID OR #itemID IS NULL)
AND CompanyID = #companyID ) D ON D.DocumentID = B.ID AND SrNo = 1
Linq2db supports window functions, and we can write details subquery and add join:
var details =
from d in _documentDetailRepository.Table
where (d.DocumentItemID == itemID || itemID == null)
&& d.CompanyID == companyID
select new
{
d.DocumentID,
SrNo = Sql.Ext.RowNumber().Over()
.PartitionBy(d.DocumentID)
.OrderBy(d.DocumentID)
.ToValue()
};
// Then your query can be extended
var query =
from a in _accountRepository.Table
join b in _documentRepository.Table on a.ID equals b.AccountID
join d in details on new { DocumentID = b.Id, SrNo = 1 } equals new { d.DocumentID, d.SrNo }
...

Do I have to loop through an IEnumerable return from Dapper even though I only return a single object?

I'm using Dapper to retrieve employee information when I select that employee from a list. Everything maps correctly, and then the rows are grouped according to employee.id. Just what I want. But Dapper returns an IEnumerable, which makes sense when I query for multiple employees and have to make multiple objects; but it makes less sense when I'm only returning the one. Is there a solution to this, or do I just need to loop through the single item? Here's my code:
public async Task<List<EmployeeModel>> GetSelectedEmployee(int selectedEmployeeID)
{
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection(GlobalConfig.CnnString("WorkDeskDB")))
{
var par = new
{
SelectedEmployeeID = selectedEmployeeID
};
var sql = #"SELECT e.id, e.FirstName, e.LastName, e.Nickname,
em.EmployeeID, em.Address, em.Type,
e.JobTitleID, jt.id, jt.Name,
p.EmployeeID, p.Number, p.Type,
ect.EmployeeID, ect.NameID, ect.InitialDate, ect.ExpirationDate,
ct.id, ct.Name
FROM dbo.Employees e
LEFT JOIN dbo.Emails em ON em.EmployeeID = e.id
LEFT JOIN dbo.JobTitles jt ON e.JobTitleID = jt.id
LEFT JOIN Phones p ON p.EmployeeID = e.id
LEFT JOIN dbo.EmployeeCertificationType ect ON ect.EmployeeID = e.id
LEFT JOIN dbo.CertificationType ct ON ect.NameID = ct.id
WHERE e.id = #SelectedEmployeeID";
var employees = await connection.QueryAsync<EmployeeModel, EmailModel, TitleModel, PhoneModel, CertificationModel, EmployeeModel>(sql, (e, em, t, p, c) =>
{
e.EmailList.Add(em);
e.JobTitle = t;
e.PhoneList.Add(p);
e.CertificationList.Add(c);
return e;
},
par, splitOn: "EmployeeID, JobTitleID, EmployeeID, EmployeeID");
var result = employees.GroupBy(e => e.ID).Select(g =>
{
var groupedEmployee = g.First();
groupedEmployee.EmailList = g.Select(e => e.EmailList.Single()).ToList();
return groupedEmployee;
});
return result.ToList();
}
}
You only have one Employee. The other rows are because an Employee can have multiple Phones, Emails and Certifications.
I suggest you do something like this:
EmployeeModel employee = null;
await connection.QueryAsync<EmployeeModel, EmailModel, TitleModel, PhoneModel, CertificationModel, EmployeeModel>(sql, (e, em, t, p, c) =>
{
if (employee is null)
{
employee = e;
employee.JobTitle = t;
}
employee.EmailList.Add(em);
employee.PhoneList.Add(p);
employee.CertificationList.Add(c);
return employee;
},
par, splitOn: "EmployeeID, JobTitleID, EmployeeID, EmployeeID");
// go on using employee, no need for the employees list ...
Then you skip the afterburning of the list.
My suggestion is that you replace the call connection.QueryAsync<EmployeeModel> by other that returns only one element, as stated here

Does the left join order matter in linq query (EF Core 2)?

I have book table which has a relationship with three tables UserFavorites, UserRates, DiscountItems and last one (DiscountItems) has a relationship with Discounts table so I want to load discount with book if it exist
When create linq left join query with this order (join userfavoirtes then discountitems then discount then userate) generated SQL code is fine as expected
But when change order like below (join userfavoirtes then userate then discountitems then discount) generated query only contain first two join only
var books = (from b in result
join uf in UnitOfWork.Context.UserFavorites
on new { b.Id, RelatedType = RelatedTypeEnum.Book, UserId = userId }
equals new { Id = uf.RelatedId, uf.RelatedType, uf.UserId } into buf
from f in buf.DefaultIfEmpty()
join di in UnitOfWork.Context.DiscountItems
on b.Id equals di.BookId into bdi
from di in bdi.DefaultIfEmpty()
join d in UnitOfWork.Context.Discounts
on (di != null ? di.DiscountId : 0) equals d.Id into bd
from d in bd.DefaultIfEmpty()
join ur in UnitOfWork.Context.UserRates
on new { b.Id, UserId = userId }
equals new { Id = ur.BookId, UserId = ur.CreatedBy } into bur
from r in bur.DefaultIfEmpty()
select new BookViewModel
{
Id = b.Id,
Title = b.Title,
Price = b.Price,
BookImage = SetDefaultImageIfNoImage(b.BookImage),
IsFavorite = f != null ? f.IsFavorite : false,
Rate = bur.Any() ? Math.Round(bur.Average(a => a.Value), 1) : 0,
Discount = d
})
.OrderBy(a => a.CategoryId)
.ThenBy(a => a.Authors)
.Take(10).ToList();
this is generated sql query
SELECT *FROM [Book] AS [a]
LEFT JOIN [UserFavorites] AS [uf] ON (([a].[Id] = [uf].[RelatedId]) AND (3 = [uf].[RelatedType])) AND [uf].[UserId] IS NULL
LEFT JOIN [DiscountItems] AS [di] ON [a].[Id] = [di].[BookId]
LEFT JOIN [Discounts] AS [d] ON CASE
WHEN [di].[Id] IS NOT NULL
THEN [di].[DiscountId] ELSE 0
END = [d].[Id]
LEFT JOIN [UserRates] AS [ur] ON ([a].[Id] = [ur].[BookId]) AND [ur].[CreatedBy] IS NULL
WHERE ((([a].[Active] = 1) AND ([a].[Id] <> 62)) AND ([a].[Status] = 1))
but when write join with this order
var books = (from b in result
join uf in UnitOfWork.Context.UserFavorites
on new { b.Id, RelatedType = RelatedTypeEnum.Book, UserId = userId }
equals new { Id = uf.RelatedId, uf.RelatedType, uf.UserId } into buf
from f in buf.DefaultIfEmpty()
join ur in UnitOfWork.Context.UserRates
on new { b.Id, UserId = userId }
equals new { Id = ur.BookId, UserId = ur.CreatedBy } into bur
from r in bur.DefaultIfEmpty()
join di in UnitOfWork.Context.DiscountItems
on b.Id equals di.BookId into bdi
from di in bdi.DefaultIfEmpty()
join d in UnitOfWork.Context.Discounts
on (di != null ? di.DiscountId : 0) equals d.Id into bd
from d in bd.DefaultIfEmpty()
select new BookViewModel
{
Id = b.Id,
Title = b.Title,
Price = b.Price,
BookImage = SetDefaultImageIfNoImage(b.BookImage),
IsFavorite = f != null ? f.IsFavorite : false,
Rate = bur.Any() ? Math.Round(bur.Average(a => a.Value), 1) : 0,
Discount = d
})
.OrderBy(a => a.CategoryId)
.ThenBy(a => a.Authors)
.Take(10).ToList();
generated SQL query contains only the first two joins
SELECT *
FROM [Book] AS [a]
LEFT JOIN [UserFavorites] AS [uf] ON (([a].[Id] = [uf].[RelatedId]) AND (3 = [uf].[RelatedType])) AND [uf].[UserId] IS NULL
LEFT JOIN [UserRates] AS [ur] ON ([a].[Id] = [ur].[BookId]) AND [ur].[CreatedBy] IS NULL
WHERE ((([a].[Active] = 1) AND ([a].[Id] <> 62)))

Entity Framework Core write MSSQL Server Extended Properties

Is it possible to create the MSSQL Server specific extended properties via Fluent-API or DataAnnotation for a Table / Schema? I would like to include my documentation into the sql server tables to satisfy our DBA.
Kind Regards
I started on an implementation using EntityFrameworkCore.Scaffolding.Handlebars but ran out of time. These are the findings:
Add
public class ScaffoldingDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
services.AddSingleton<IDatabaseModelFactory, SqlServerDatabaseModelFactory2>();
var options = ReverseEngineerOptions.DbContextAndEntities;
services.AddHandlebarsScaffolding(options);
// https://github.com/TrackableEntities/EntityFrameworkCore.Scaffolding.Handlebars/issues/30
Handlebars.RegisterHelper("f-pn", FormatPropertyName);
}
void FormatPropertyName(TextWriter writer, object context, object[] args)
{
writer.WriteSafeString(args[0].ToString());
}
}
Copy SqlServerDatabaseModelFactory from
SqlServerDatabaseModelFactory and customize it with a function going like this
DbConnection connection,
IReadOnlyList<DatabaseTable> tables,
string tableFilter,
IReadOnlyDictionary<string, (string storeType, string typeName)> typeAliases)
{
using (var command = connection.CreateCommand())
{
var commandText = #"
SELECT u.name AS [table_schema],
t.name AS [table_name],
td.value AS [table_desc],
c.name AS [column_name],
cd.value AS [column_desc]
FROM sysobjects t
INNER JOIN sysusers u
ON u.uid = t.uid
LEFT OUTER JOIN sys.extended_properties td
ON td.major_id = t.id
AND td.minor_id = 0
AND td.name = 'MS_Description'
INNER JOIN syscolumns c
ON c.id = t.id
LEFT OUTER JOIN sys.extended_properties cd
ON cd.major_id = c.id
AND cd.minor_id = c.colid
AND cd.name = 'MS_Description'
WHERE t.type = 'u'
ORDER BY t.name, c.colorder";
command.CommandText = commandText;
using (var reader = command.ExecuteReader())
{
var tableColumnGroups = reader.Cast<DbDataRecord>()
.GroupBy(
ddr => (tableSchema: ddr.GetValueOrDefault<string>("table_schema"),
tableName: ddr.GetValueOrDefault<string>("table_name")));
foreach (var tableColumnGroup in tableColumnGroups)
{
var tableSchema = tableColumnGroup.Key.tableSchema;
var tableName = tableColumnGroup.Key.tableName;
var table = tables.Single(t => t.Schema == tableSchema && t.Name == tableName);
foreach (var dataRecord in tableColumnGroup)
{
var columnName = dataRecord.GetValueOrDefault<string>("column_name");
var tableDesc = dataRecord.GetValueOrDefault<string>("table_desc");
var columnDesc = dataRecord.GetValueOrDefault<string>("column_desc");
//_logger.ColumnFound(
// DisplayName(tableSchema, tableName),
// columnName,
// ordinal,
// DisplayName(dataTypeSchemaName, dataTypeName),
// maxLength,
// precision,
// scale,
// nullable,
// isIdentity,
// defaultValue,
// computedValue);
table.Description = tableDesc; ???
table.Columns.FirstOrDefault(x => x.Name == columnName)?.Description = columnDesc; ???;
}
}
}
}
}
Make a dictionary of the table and column descriptions and use Handlebars Helpers/Transformers EntityFrameworkCore.Scaffolding.Handlebars

How to do this SQL in LINQ

Morning i would like to know how to do this bit of (MS) SQL in LINQ...
SELECT CONVERT(CHAR(10),orderDate,110) AS OrderDate,
SUM(1) AS TotalOrders
FROM Orders
WHERE OrderDate>getdate()-30
GROUP BY CONVERT(CHAR(10),orderDate,110)
ORDER BY OrderDate DESC
Many thanks in advance.
UPDATE
I ended using and edited version of the solutions provided below, thought i would share it...
using (DataDataContext dc = new DataDataContext())
{
var query = from o in dc.Orders
where o.orderDate > DateTime.Now.AddDays(-30)
let dt = o.orderDate
group o by new DateTime(dt.Year, dt.Month, dt.Day) into g
select new OrderCounts
{
OrderDate = String.Format("{0:d}", g.Key.Date),
TotalOrders = g.Count()
};
query.GroupBy(o => o.OrderDate);
query.OrderBy(o => o.OrderDate);
return query.ToList();
}
var result =(from s in Orders
where s.OrderDate > DateTime.Now.AddDays(-30)
group s by new { date = s.OrderDate.ToString() } into g
// or use ((DateTime)s.OrderDate).ToShortDateString() instead of s.OrderDate.ToString()
select new
{
OrderDate = g.Key.date,
TotalOrders = g.Count()
}).OrderByDescending(x=>x.OrderDate);;
The query will be
var query = from o in res
where o.OrderDate > DateTime.Now.AddDays(-30)
orderby o.OrderDate descending
group o by o.OrderDate into g
select new
{
OrderDate = g.Key.Date.ToString("MM/dd/yyyy")
,
TotalOrders = g.Sum(i=> 1)
};
OR by Lambda expression
var query= res
.Where(i => i.OrderDate > DateTime.Now.AddDays(-30))
.OrderByDescending(o => o.OrderDate)
.GroupBy(g => g.OrderDate)
.Select(s => new { OrderDate = s.Key.Date.ToString("MM/dd/yyyy"), TotalOrders = s.Sum(i => 1) });

Resources