I have a query which is working fine. I want to convert it to use Cases and Join.
SELECT id, Ticket_id, serial_no, operator,
(SELECT value FROM Forms_Details where FORM_ID = cv.id and status = 'ACTIVE' and key = 'COMPUTER_TYPE') as COMPUTER_TYPE,
(SELECT value FROM Forms_Details where FORM_ID = cv.id and status = 'ACTIVE' and key = 'BRAND') as BRAND,
(SELECT value FROM Forms_Details where FORM_ID = cv.id and status = 'ACTIVE' and key = 'MODEL') as MODEL,
status, datetime
FROM Form cv
where status = 'ACTIVE' order by id desc;
If you want to join, then join. What keeps you from doing so?
SELECT
f.id,
f.Ticket_id,
f.serial_no,
f.operator,
ct.value AS computer_type,
b.value AS brand,
m.value AS model,
f.status,
f.datetime
FROM form f
LEFT JOIN forms_details ct ON ct.form_id = f.id AND ct.status = 'ACTIVE' AND ct.key = 'COMPUTER_TYPE'
LEFT JOIN forms_detailswhere b ON b.form_id = f.id AND b.status = 'ACTIVE' AND b.key = 'BRAND'
LEFT JOIN forms_detailswhere m ON m.form_id = f.id AND m.status = 'ACTIVE' AND m.key = 'MODEL'
WHERE f.status = 'ACTIVE'
ORDER BY f.id DESC;
Or, in order not to read the same table twice, you can use conditional aggregation to get the values from the table:
SELECT
f.id,
f.Ticket_id,
f.serial_no,
f.operator,
ct.value AS computer_type,
fdw.brand,
fdw.model,
f.status,
f.datetime
FROM form f
LEFT JOIN forms_details ct ON ct.form_id = f.id AND ct.status = 'ACTIVE' AND ct.key = 'COMPUTER_TYPE'
LEFT JOIN
(
SELECT
form_id,
MAX(CASE WHEN key = 'BRAND' THEN value END) AS brand,
MAX(CASE WHEN key = 'MODEL' THEN value END) AS model
FROM forms_detailswhere
WHERE status = 'ACTIVE' AND key IN ('BRAND', 'MODEL')
GROUP BY form_id
) fdw ON fdw.form_id = f.id
WHERE f.status = 'ACTIVE'
ORDER BY f.id DESC;
You can PIVOT and then join:
SELECT cv.id,
cv.Ticket_id,
cv.serial_no,
cv.operator,
fd.computer_type,
fd.brand,
fd.model,
cv.status,
cv.datetime
FROM Form cv
LEFT OUTER JOIN (
SELECT *
FROM (
SELECT form_id, key, value
FROM Form_Details
WHERE status = 'ACTIVE'
)
PIVOT (
MAX(value)
FOR key IN (
'BRAND' AS brand,
'MODEL' AS model,
'COMPUTER_TYPE' AS computer_type
)
)
) fd
ON (cv.id = fd.form_id)
where cv.status = 'ACTIVE'
order by id desc;
How to do the following with laravel ORM?
select f.type, f.variety, f.price
from (
select type, min(price) as minprice
from fruits group by type
) as x inner join fruits as f on f.type = x.type and f.price = x.minprice;
$subQuery = "(select type, min(price) as minprice from fruits group by type) as x";
$query = DB::table('fruits')::selectRaw( "fruits.type,fruits.variety,x.minprice" );
$query->join( DB::raw($subQuery), function ($join) {
$join->on ( 'fruits.type', '=', 'x.type' );
});
$data = $query->get();
I have not tested the code but hopes it works.
I'm trying to get the sum of fees per customer per month from the mysql sakila database.
My SQL query looks like this:
select first_name, last_name, MONTHNAME(payment_date) as Month, sum(amount) as FeeSum
from customer c
join payment p on c.customer_id = p.customer_id
where (payment_date between '2005-01-01' AND '2005-06-30')
group by c.customer_id, Month
order by Month desc, FeeSum desc;
I did this in linqpad
var q11 = from c in Customer
from p in Payment
where c.Customer_id == p.Customer_id && ((DateTime)p.Payment_date) > DateTime.Parse("2005-01-01") && ((DateTime)p.Payment_date) < DateTime.Parse("2005-06-30")
group new {c, p} by new {((DateTime)p.Payment_date).Month, p.Customer_id} into grp
select new {
Month = grp.Key.Month,
FeeSum = grp.Sum(s => s.p.Amount),
} into selection
orderby selection.Month, selection.FeeSum descending
select selection;
q11.Dump();
Which works for the FeeSum and the Month, but I can't figure out how to get the first_name and the lasT_name of the customer
Since the members of each grp all have the same Customer, just pick any one:
var q11 = from c in Customer
from p in Payment
where c.Customer_id == p.Customer_id && ((DateTime)p.Payment_date) > DateTime.Parse("2005-01-01") && ((DateTime)p.Payment_date) < DateTime.Parse("2005-06-30")
group new { c, p } by new { ((DateTime)p.Payment_date).Month, p.Customer_id } into grp
select new {
grp.First().first_name,
grp.First().last_name,
Month = grp.Key.Month,
FeeSum = grp.Sum(s => s.p.Amount),
} into selection
orderby selection.Month, selection.FeeSum descending
select selection;
Note: I prefer to use let rather than select twice, but it is the same thing internally, I believe:
var q11 = from c in Customer
from p in Payment
where c.Customer_id == p.Customer_id && ((DateTime)p.Payment_date) > DateTime.Parse("2005-01-01") && ((DateTime)p.Payment_date) < DateTime.Parse("2005-06-30")
group new { c, p } by new { ((DateTime)p.Payment_date).Month, p.Customer_id } into grp
let FeeSum = grp.Sum(s => s.p.Amount)
orderby grp.Key.Month, FeeSum descending
select new {
grp.First().first_name,
grp.First().last_name,
Month = grp.Key.Month,
FeeSum
};
This works...
$sql30 = "SELECT * FROM statuses LEFT JOIN friends ON statuses.userID=friends.lowerFriendID WHERE friends.lowerFriendID != :user_ida
UNION SELECT * FROM statuses RIGHT JOIN friends ON statuses.userID=friends.higherFriendID WHERE friends.higherFriendID != :user_idb ORDER BY userID DESC";
But I don't need to sort by userID, this does not work...
$sql30 = "SELECT * FROM statuses LEFT JOIN friends ON statuses.userID=friends.lowerFriendID WHERE friends.lowerFriendID != :user_ida
UNION SELECT * FROM statuses RIGHT JOIN friends ON statuses.userID=friends.higherFriendID WHERE friends.higherFriendID != :user_idb ORDER BY id DESC";
nor does this...
$sql30 = "SELECT * FROM statuses LEFT JOIN friends ON statuses.userID=friends.lowerFriendID WHERE friends.lowerFriendID != :user_ida
UNION SELECT * FROM statuses RIGHT JOIN friends ON statuses.userID=friends.higherFriendID WHERE friends.higherFriendID != :user_idb ORDER BY `id` DESC";
nor does this...
$sql30 = "SELECT * FROM statuses LEFT JOIN friends ON statuses.userID=friends.lowerFriendID WHERE friends.lowerFriendID != :user_ida
UNION SELECT * FROM statuses RIGHT JOIN friends ON statuses.userID=friends.higherFriendID WHERE friends.higherFriendID != :user_idb ORDER BY timestamp DESC";
I need to order by either "id" or "timestamp" DESC so that the most recent post shows first. Your help is much appreciated. Thank You!
Is there a way to override default MVC3 WebGrid sorting behavior to call my controller (which will perform server side sorting and return the data) when sort is called?
Thanks for any help!
You can pass the server side sorted data to the webgrid, along with the info on how many records there are. http://msdn.microsoft.com/en-us/magazine/hh288075.aspx has a helpful walkthrough. I'm doing database level sorting/filtering/paging to minimize the amount of data being passed around. I don't think my webserver would love me if I passed all 70,000 objects a customer has just so they can see the 25 on page 1. This is pretty much everything you need, except the very lightweight view model that just wraps your IEnumerable collection with some extra paging data.
Since web grid just uses the query string vars to decide what to do you need to use a get method form. And you need to include the sort field/direction in querystring in a way webgrid knows how to read it. So you end up with urls like localhost/example/admin/thing?thingName=Hyatt&City=&State=TX&Country=&sort=city&sortdir=ASC
Controller:
public ActionResult Index(string thingName, string city, string state, string country, int page = 1)
{
const int pageSize = 25;
int totalRecords = 0;
IEnumerable<Thing> things = ThingModel.GetPagedSortedLocationsForCustomer(customerId, sort, sortdir, out totalRecords, pageSize, page, thingName, city, state, country);
PagedThings viewModel = new PagedThings(pageSize, page, totalRecords, things);
return View(viewModel);
}
View:
#model ExampleCompany.Area.ViewModels.PagedThings
#{
using (Html.BeginForm("Index", "ThingaMaBob", System.Web.Mvc.FormMethod.Get))
{
<label for="ThingName">ThingName</label>#Html.TextBox("ThingName", "")
<label for="City">City</label>#Html.TextBox("City", "")
<label for="State">State</label>#Html.TextBox("State", "")
<label for="Country">Country</label>#Html.TextBox("Country", "")
<input type="submit" value="Filter" />
<br />
var grid = new WebGrid(canPage: true, rowsPerPage: Model.PageSize, canSort: true);
grid.Bind(Model.Things, rowCount: Model.TotalRows, autoSortAndPage: false);
grid.Pager(WebGridPagerModes.All);
#grid.GetHtml(htmlAttributes: new { id = "grid"},
columns: grid.Columns(
//ommitted
grid.Column("thingName", "Thing"),
));
Html.Hidden(grid.SortFieldName, grid.SortColumn);
Html.Hidden(grid.SortDirectionFieldName, grid.SortDirection == SortDirection.Ascending ? "ASC" : "DESC");
}
Model:
public static IEnumerable<Thing> GetPagedSortedThingsForCustomer(int customerid, String sortby, String sorttype, out int totalRecords, int pageSize, int pageIndex, string thingName, string city, string state, string country)
{
var tmp = new List<Thing>();
int total = 0;
dynamic dr = OurDBUtility.ReturnDR("ExampleProc_GetThingsSortedPaged", ConnectionInfo.ExampleConnection, customerid, sortby, sorttype, pageSize, pageIndex, thingName, city, state, country);
{
while (dr.Read())
{
var tmpThing = new Thing();
tmpThing.LoadFromDr(dr);
tmp.Add(tmpThing);
if (total == 0)
{
total = (int)dr["TOTAL_THINGS"];
}
}
}
totalRecords = total;
return tmp;
}
Proc with dynamic sql - yes, you could use Linq-to-Sql or other techniques if you wanted to, but i'm old school:
CREATE PROCEDURE ExampleProc_GetThingsSortedPaged
( #CustomerId int
, #sortby nvarchar(60)
, #sorttype nvarchar(60)
, #pageSize int
, #pageIndex int
, #thingName nvarchar(255) = null
, #city nvarchar(30) = null
, #state nvarchar(30) = null
, #country char(2) = null
)
as
DECLARE #strSql nvarchar(3000)
--calculate paging rows
declare #startRow int, #endRow int
--e.g. if you have a page size of 10, page 1 = 1 - 10, page 2 = 11 -20
set #startRow = ((#pageIndex - 1) * #pageSize) + 1
set #endRow = #startRow + #pageSize - 1
if #thingName = ''
set #thingName = null
if #city = ''
set #city = null
if #state = ''
set #state = null
if #country = ''
set #country = null
--return total for webgrid, accounting for filter
declare #totalThings int
select #totalThings = COUNT(*)
from EXAMPLE_TABLE T with(nolock)
where CUSTOMER_ID = #CustomerId
AND (T.THING_NAME LIKE #thingName + '%' OR #thingName is null)
AND (T.CITY LIKE #city + '%' or #city is null)
AND (T.STATE LIKE #state + '%' or #state is null)
AND (T.COUNTRY = #country or #country is null)
DECLARE #ParameterDefinition AS NVARCHAR(200)
set #ParameterDefinition = '#totalThings int, #CustomerId INT, #startRow INT, #endRow INT, #thingName nvarchar(255), #city nvarchar(30), #state nvarchar(30), #country char(2)'
--When we need to do dynamic sql it is better to use paramterization, but you cannot do (ORDER BY #sortBy).
SET #strSql = N'SELECT * from
(
select ROW_NUMBER() OVER (ORDER BY T.' + #sortby + ' ' + #sorttype + ') as Row,
#totalThings [TOTAL_THINGS],
T.THING_ID, T.THING_NAME, T.ADDRESS, T.CITY, T.STATE,
T.ZIP_CODE, T.COUNTRY
FROM EXAMPLE_TABLE T
WHERE T.CUSTOMER_ID = #CustomerId
AND (T.THING_NAME LIKE #thingName + ''%'' OR #thingName is null)
AND (T.CITY LIKE #city + ''%'' or #city is null)
AND (T.STATE LIKE #state + ''%'' or #state is null)
AND (T.COUNTRY = #country or #country is null)
) paged
where Row between #startRow and #endRow
ORDER BY Row'
--print #strSql
EXECUTE sp_executesql #strSql, #ParameterDefinition, #totalThings, #CustomerId, #startRow, #endRow, #thingName, #city, #state, #country
GO
Proc with CTE:
CREATE PROCEDURE ExampleProc_GetThingsSortedPaged
( #CustomerID int
, #sortby nvarchar(60)
, #sorttype nvarchar(60)
, #pageSize int = 25
, #pageIndex int = 1
, #thingName nvarchar(255) = null
, #city varchar(30) = null
, #state nvarchar(30) = null
, #country char(2) = null
)
as
declare #startRow int
declare #endRow int
SET #startRow = ((#pageIndex - 1) * #pageSize) + 1;
SET #endRow = #startRow + #pageSize - 1;
set #sortby = replace(LOWER(#sortby), '_', '')
SET #sorttype = LOWER(#sorttype)
if #sorttype != 'asc' and #sorttype != 'desc'
begin
set #sorttype = 'asc'
end
;with cte_things as (
SELECT
CASE
WHEN #sortby ='country' AND #sorttype = 'asc' then row_number() over (order by C.COUNTRY_NAME ASC)
WHEN #sortby ='country' AND #sorttype = 'desc' then row_number() over (order by C.COUNTRY_NAME DESC)
WHEN #sortby ='state' AND #sorttype = 'asc' then row_number() over (order by STATE ASC)
WHEN #sortby ='state' AND #sorttype = 'desc' then row_number() over (order by STATE DESC)
WHEN #sortby ='city' AND #sorttype = 'asc' then row_number() over (order by CITY ASC)
WHEN #sortby ='city' AND #sorttype = 'desc' then row_number() over (order by CITY DESC)
WHEN #sortby ='thingname' AND #sorttype = 'desc' then row_number() over (order by THING_NAME DESC)
ELSE row_number() over (order by THING_NAME ASC)
END AS Row
,T.THING_ID, T.THING_NAME, T.THING_TYPE, T.ADDRESS, T.CITY, T.STATE
, T.ZIP_CODE, T.COUNTRY_CODE, C.COUNTRY_NAME, T.PHONE_NUMBER
, T.LATITUDE, T.LONGITUDE
FROM EXAMPLE_TABLE L
join COUNTRIES C
on C.COUNTRY_CODE = L.COUNTRY_CODE
where
T.CUSTOMER_ID = #CustomerId
and L.CITY = ISNULL(#city, CITY)
and L.STATE = ISNULL(#state, STATE)
and L.COUNTRY_CODE = ISNULL(#country, L.COUNTRY_CODE)
and L.THING_NAME = ISNULL(#thingName, THING_NAME)
)
, cte_total as (select COUNT(*) as TOTAL_THINGS from cte_things)
, cte_all as (select cte_things.*, cte_total.TOTAL_THINGS from cte_things cross join cte_total)
SELECT * FROM cte_all
where
Row >= #startRow
and Row <= #endRow
ORDER BY Row
GO